xref: /csrg-svn/lib/libc/db/hash/hash_page.c (revision 50058)
146372Sbostic /*-
246372Sbostic  * Copyright (c) 1990 The Regents of the University of California.
346372Sbostic  * All rights reserved.
446372Sbostic  *
546372Sbostic  * This code is derived from software contributed to Berkeley by
646372Sbostic  * Margo Seltzer.
746372Sbostic  *
846372Sbostic  * %sccs.include.redist.c%
946372Sbostic  */
1046372Sbostic 
1146372Sbostic #if defined(LIBC_SCCS) && !defined(lint)
12*50058Sbostic static char sccsid[] = "@(#)hash_page.c	5.13 (Berkeley) 06/17/91";
1346372Sbostic #endif /* LIBC_SCCS and not lint */
1446372Sbostic 
1546372Sbostic /******************************************************************************
1646372Sbostic PACKAGE:  hashing
1746372Sbostic 
1846372Sbostic DESCRIPTION:
1946372Sbostic 	Page manipulation for hashing package.
2046372Sbostic 
2146372Sbostic ROUTINES:
2246372Sbostic     External
2346372Sbostic 	__get_page
2446372Sbostic 	__add_ovflpage
2546372Sbostic     Internal
2646372Sbostic 	overflow_page
2746372Sbostic 	open_temp
2846372Sbostic ******************************************************************************/
2946372Sbostic 
3046372Sbostic #include <sys/param.h>
3146562Sbostic #include <fcntl.h>
3246416Sbostic #include <signal.h>
3346372Sbostic #include <assert.h>
3446372Sbostic #include <errno.h>
3546372Sbostic #include <db.h>
3646372Sbostic #include <stdio.h>
3746562Sbostic #include <stdlib.h>
3846562Sbostic #include <string.h>
3946500Sbostic #include <unistd.h>
4046372Sbostic #include "hash.h"
4146372Sbostic #include "page.h"
4246372Sbostic 
4346372Sbostic /* Externals */
4446372Sbostic /* buf.c */
4546372Sbostic extern BUFHEAD	*__get_buf();
4646372Sbostic extern void __reclaim_buf();
4746372Sbostic 
4846372Sbostic /* big.c */
4946372Sbostic extern int __big_split();
5046372Sbostic extern int __big_insert();
5146372Sbostic extern int __big_delete();
5246372Sbostic extern int __find_bigpair();
5346372Sbostic 
5446372Sbostic /* dynahash.c */
5547250Sbostic extern	u_int	__call_hash();
5646372Sbostic extern	int	__expand_table();
5746372Sbostic 
5846372Sbostic /* my externals */
5946372Sbostic extern int __get_page();
6046372Sbostic extern BUFHEAD *__add_ovflpage();
6146372Sbostic extern int __split_page();
6246372Sbostic extern int __addel();
6346372Sbostic 
6446372Sbostic /* my internals */
6546372Sbostic static u_short overflow_page();
6646372Sbostic static int open_temp();
6746535Sbostic static int ugly_split();
6846372Sbostic static void squeeze_key();
6946372Sbostic static void putpair();
7047250Sbostic static u_long *fetch_bitmap();
7146372Sbostic 
7246372Sbostic #ifdef HASH_STATISTICS
7346372Sbostic extern long hash_accesses, hash_collisions, hash_expansions, hash_overflows;
7446372Sbostic #endif
7546372Sbostic #define	PAGE_INIT(P)					\
7646372Sbostic {							\
7746372Sbostic     ((u_short *)P)[0] = 0;				\
7846372Sbostic     ((u_short *)P)[1] = hashp->BSIZE - 3 * sizeof(u_short);	\
7946372Sbostic     ((u_short *)P)[2] = hashp->BSIZE;			\
8046372Sbostic }
8146372Sbostic 
8246372Sbostic /*
8346372Sbostic 	This is called AFTER we have verified that there is room on the
8446372Sbostic 	page for the pair (PAIRFITS has returned true) so we go right
8546372Sbostic 	ahead and start moving stuff on.
8646372Sbostic */
8746372Sbostic static void
8846372Sbostic putpair(p, key, val)
8946372Sbostic char *p;
9046372Sbostic DBT *key;
9146372Sbostic DBT *val;
9246372Sbostic {
9346372Sbostic 	register u_short n;
9446372Sbostic 	register u_short off;
9546372Sbostic 	register u_short *bp = (u_short *) p;
9646372Sbostic 
9746372Sbostic /* enter the key first */
9846372Sbostic 	n = bp[0];
9946372Sbostic 
10046372Sbostic 	off = OFFSET(bp) - key->size;
10146372Sbostic 	bcopy( key->data, p+off, key->size );
10246372Sbostic 	bp[++n] = off;
10346372Sbostic 
10446372Sbostic /* now the data */
10546372Sbostic 	off -= val->size;
10646372Sbostic 	bcopy(val->data,  p + off, val->size);
10746372Sbostic 	bp[++n] = off;
10846372Sbostic 
10946372Sbostic /* adjust page info */
11046372Sbostic 	bp[0] = n;
11146372Sbostic 	bp[n+1] = off - ((n+3)*sizeof(u_short));
11246372Sbostic 	bp[n+2] = off;
11346372Sbostic 	return;
11446372Sbostic }
11546372Sbostic /*
11646372Sbostic     0 OK
11746372Sbostic     -1 error
11846372Sbostic */
11946372Sbostic extern int
12046372Sbostic __delpair(bufp, ndx)
12146372Sbostic BUFHEAD *bufp;
12246372Sbostic register int ndx;
12346372Sbostic {
12446372Sbostic 	register u_short *bp = (u_short *) bufp->page;
12546372Sbostic 	register int n = bp[0];
12646372Sbostic 	register u_short newoff;
12746372Sbostic 	u_short pairlen;
12846372Sbostic 
12946372Sbostic 	if ( bp[ndx+1] < REAL_KEY ) return ( __big_delete ( bufp, ndx ) );
13046372Sbostic 	if ( ndx != 1 ) newoff = bp[ndx-1];
13146372Sbostic 	else newoff = hashp->BSIZE;
13246372Sbostic 	pairlen = newoff - bp[ndx+1];
13346372Sbostic 
13446372Sbostic 	if ( ndx != (n-1) ) {
13546372Sbostic 		/* Hard Case -- need to shuffle keys */
13646372Sbostic 		register int i;
13746372Sbostic 		register char *src = bufp->page + (int)OFFSET(bp);
13846372Sbostic 		register char *dst = src + (int)pairlen;
13946372Sbostic 		bcopy ( src, dst, bp[ndx+1] - OFFSET(bp) );
14046372Sbostic 
14146372Sbostic 		/* Now adjust the pointers */
14246372Sbostic 		for ( i = ndx+2; i <= n; i += 2 ) {
14346372Sbostic 		    if ( bp[i+1]  == OVFLPAGE ) {
14446372Sbostic 			bp[i-2] = bp[i];
14546372Sbostic 			bp[i-1] = bp[i+1];
14646372Sbostic 		    } else {
14746372Sbostic 			bp[i-2] = bp[i] + pairlen;
14846372Sbostic 			bp[i-1] = bp[i+1] + pairlen;
14946372Sbostic 		    }
15046372Sbostic 		}
15146372Sbostic 	}
15246372Sbostic 
15346372Sbostic 	/* Finally adjust the page data */
15446372Sbostic 	bp[n] = OFFSET(bp) + pairlen;
15546372Sbostic 	bp[n-1] = bp[n+1] + pairlen + 2 * sizeof(u_short);
15646372Sbostic 	bp[0] = n-2;
15746372Sbostic 	hashp->NKEYS--;
15846372Sbostic 
15946372Sbostic 	bufp->flags |= BUF_MOD;
16046372Sbostic 	return (0);
16146372Sbostic }
16246372Sbostic /*
16346372Sbostic     -1 ==> Error
16446372Sbostic     0 ==> OK
16546372Sbostic */
16646372Sbostic extern int
16746372Sbostic __split_page(obucket, nbucket)
16847250Sbostic u_int obucket;
16947250Sbostic u_int nbucket;
17046372Sbostic {
17146372Sbostic 	DBT key;
17246372Sbostic 	DBT val;
17346372Sbostic 
17446372Sbostic 	register BUFHEAD *new_bufp;
17546372Sbostic 	register BUFHEAD *old_bufp;
17646372Sbostic 	register u_short *ino;
17746372Sbostic 	register char	*np;
17846460Sbostic 	int n;
17946460Sbostic 	int ndx;
18046460Sbostic 	int retval;
18146372Sbostic 	char	*op;
18246372Sbostic 
18346372Sbostic 	u_short copyto = (u_short)hashp->BSIZE;
18446460Sbostic 	u_short diff;
18546372Sbostic 	u_short off = (u_short)hashp->BSIZE;
18646372Sbostic 	u_short moved;
18746372Sbostic 
18846372Sbostic 	old_bufp = __get_buf ( obucket, NULL, 0 );
18946372Sbostic 	new_bufp = __get_buf ( nbucket, NULL, 0 );
19046372Sbostic 
19146460Sbostic 	old_bufp->flags |= (BUF_MOD|BUF_PIN);
19246460Sbostic 	new_bufp->flags |= (BUF_MOD|BUF_PIN);
19346372Sbostic 
19446372Sbostic 	ino = (u_short *)(op = old_bufp->page);
19546372Sbostic 	np = new_bufp->page;
19646372Sbostic 
19746372Sbostic 	moved = 0;
19846372Sbostic 
19946372Sbostic 	for (n = 1, ndx = 1; n < ino[0]; n+=2) {
20046372Sbostic 		if ( ino[n+1] < REAL_KEY ) {
20146460Sbostic 		    retval = ugly_split( obucket, old_bufp, new_bufp,
20246460Sbostic 					 copyto, moved );
20346460Sbostic 		    old_bufp->flags &= ~BUF_PIN;
20446460Sbostic 		    new_bufp->flags &= ~BUF_PIN;
20546460Sbostic 		    return(retval);
20646460Sbostic 
20746372Sbostic 		}
20846950Sbostic 		key.data = (u_char *)op + ino[n];
20946372Sbostic 		key.size = off - ino[n];
21046372Sbostic 
21146372Sbostic 		if ( __call_hash ( key.data, key.size ) == obucket ) {
21246372Sbostic 		    /* Don't switch page */
21346372Sbostic 		    diff = copyto - off;
21446372Sbostic 		    if ( diff ) {
21546372Sbostic 			copyto = ino[n+1] + diff;
21646372Sbostic 			bcopy ( op + ino[n+1], op + copyto,  off-ino[n+1]);
21746372Sbostic 			ino[ndx] = copyto + ino[n] - ino[n+1];
21846372Sbostic 			ino[ndx+1] = copyto;
21946372Sbostic 		    } else copyto = ino[n+1];
22046372Sbostic 		    ndx += 2;
22146372Sbostic 		} else {
22246372Sbostic 		    /* Switch page */
22346950Sbostic 		    val.data = (u_char *)op + ino[n+1];
22446372Sbostic 		    val.size = ino[n] - ino[n+1];
22546372Sbostic 		    putpair( np, &key, &val);
22646372Sbostic 		    moved +=2;
22746372Sbostic 		}
22846372Sbostic 
22946372Sbostic 		off = ino[n+1];
23046372Sbostic 	}
23146372Sbostic 
23246372Sbostic 	/* Now clean up the page */
23346372Sbostic 	ino[0] -= moved;
23446372Sbostic 	FREESPACE(ino) = copyto - sizeof(u_short) * (ino[0]+3);
23546372Sbostic 	OFFSET(ino) = copyto;
23646372Sbostic 
23746372Sbostic #ifdef DEBUG3
23846372Sbostic 	fprintf(stderr, "split %d/%d\n",
23946372Sbostic 	       ((u_short *) np)[0] / 2,
24046372Sbostic 	       ((u_short *) op)[0] / 2);
24146372Sbostic #endif
24246460Sbostic 	/* unpin both pages */
24346460Sbostic 	old_bufp->flags &= ~BUF_PIN;
24446460Sbostic 	new_bufp->flags &= ~BUF_PIN;
24546372Sbostic 	return(0);
24646372Sbostic }
24746372Sbostic /*
24846372Sbostic     0 ==> success
24946372Sbostic     -1 ==> failure
25046372Sbostic 
25146460Sbostic     Called when we encounter an overflow or big key/data page during
25246460Sbostic     split handling.
25346460Sbostic     This is special cased since we have to begin checking whether
25446372Sbostic     the key/data pairs fit on their respective pages and because
25546372Sbostic     we may need overflow pages for both the old and new pages
25646460Sbostic 
25746460Sbostic     The first page might be a page with regular key/data pairs
25846460Sbostic     in which case we have a regular overflow condition and just
25946460Sbostic     need to go on to the next page or it might be a big key/data
26046460Sbostic     pair in which case we need to fix the big key/data pair.
26146372Sbostic */
26246372Sbostic static int
26346372Sbostic ugly_split( obucket, old_bufp, new_bufp, copyto, moved )
26447250Sbostic u_int	obucket;	/* Same as __split_page */
26546372Sbostic BUFHEAD	*old_bufp;
26646372Sbostic BUFHEAD	*new_bufp;
26746372Sbostic u_short	copyto;		/* First byte on page which contains key/data values */
26846372Sbostic int	moved;		/* number of pairs moved to new page */
26946372Sbostic {
27046372Sbostic     register BUFHEAD *bufp = old_bufp;		/* Buffer header for ino */
27146372Sbostic     register u_short	*ino = (u_short *)old_bufp->page;
27246372Sbostic 						/* Page keys come off of */
27346372Sbostic     register u_short	*np = (u_short *)new_bufp->page;	/* New page */
27446372Sbostic     register u_short	*op = (u_short *)old_bufp->page;
27546372Sbostic 						/* Page keys go on to if they
27646372Sbostic 						   aren't moving */
27746372Sbostic 
27846372Sbostic     char	*cino;				/* Character value of ino */
27946372Sbostic     BUFHEAD	*last_bfp = NULL;		/* Last buffer header OVFL which
28046372Sbostic 						   needs to be freed */
28146372Sbostic     u_short	ov_addr, last_addr = 0;
28246372Sbostic     u_short	n;
28346372Sbostic     u_short	off;
28446372Sbostic 
28546372Sbostic     DBT	key, val;
28646372Sbostic     SPLIT_RETURN	ret;
28746372Sbostic 
28846372Sbostic     n = ino[0]-1;
28946372Sbostic     while ( n < ino[0] ) {
29046502Sbostic 	if ( ino[2] < REAL_KEY && ino[2] != OVFLPAGE ) {
29146502Sbostic 	    if (__big_split (old_bufp, new_bufp, bufp, ov_addr, obucket, &ret)) {
29246502Sbostic 		return(-1);
29346502Sbostic 	    }
29446502Sbostic 	    old_bufp = ret.oldp;
29546502Sbostic 	    if ( !old_bufp ) return(-1);
29646502Sbostic 	    op = (u_short *)old_bufp->page;
29746502Sbostic 	    new_bufp = ret.newp;
29846502Sbostic 	    if ( !new_bufp ) return(-1);
29946502Sbostic 	    np = (u_short *)new_bufp->page;
30046502Sbostic 	    bufp = ret.nextp;
30146502Sbostic 	    if ( !bufp ) return(0);
30246502Sbostic 	    cino = (char *)bufp->page;
30346502Sbostic 	    ino = (u_short *)cino;
30446502Sbostic 	    last_bfp = ret.nextp;
30546502Sbostic 	} else if ( ino[n+1] == OVFLPAGE ) {
30646372Sbostic 	    ov_addr = ino[n];
30746372Sbostic 	    /*
30846372Sbostic 		Fix up the old page -- the extra 2 are the fields which
30946372Sbostic 		contained the overflow information
31046372Sbostic 	    */
31146372Sbostic 	    ino[0] -= (moved + 2);
31246372Sbostic 	    FREESPACE(ino) = copyto - sizeof(u_short) * (ino[0]+3);
31346372Sbostic 	    OFFSET(ino) = copyto;
31446372Sbostic 
31546372Sbostic 	    bufp = __get_buf ( ov_addr, bufp, 0 );
31646372Sbostic 	    if ( !bufp ) return(-1);
31746372Sbostic 
31846372Sbostic 	    ino = (u_short *)bufp->page;
31946372Sbostic 	    n = 1;
32046372Sbostic 	    copyto = hashp->BSIZE;
32146372Sbostic 	    moved = 0;
32246372Sbostic 
32346372Sbostic 	    if ( last_bfp ) {
32446372Sbostic 		__free_ovflpage( last_bfp);
32546372Sbostic 	    }
32646372Sbostic 	    last_bfp = bufp;
32746372Sbostic 	}
32846372Sbostic 
32946372Sbostic 
33046460Sbostic 	/* Move regular sized pairs of there are any */
33146372Sbostic 	off = hashp->BSIZE;
33246372Sbostic 	for ( n = 1; (n < ino[0]) && (ino[n+1] >= REAL_KEY); n += 2 ) {
33346372Sbostic 	    cino = (char *)ino;
33446950Sbostic 	    key.data = (u_char *)cino + ino[n];
33546372Sbostic 	    key.size = off - ino[n];
33646950Sbostic 	    val.data = (u_char *)cino + ino[n+1];
33746372Sbostic 	    val.size = ino[n] - ino[n+1];
33846372Sbostic 	    off = ino[n+1];
33946372Sbostic 
34046372Sbostic 	    if ( __call_hash ( key.data, key.size ) == obucket ) {
34146372Sbostic 		/* Keep on old page */
34246372Sbostic 		if (PAIRFITS(op,(&key),(&val))) putpair((char *)op, &key, &val);
34346372Sbostic 		else {
34446372Sbostic 		    old_bufp = __add_ovflpage ( old_bufp );
34546372Sbostic 		    if ( !old_bufp ) return(-1);
34646372Sbostic 		    op = (u_short *)old_bufp->page;
34746372Sbostic 		    putpair ((char *)op, &key, &val);
34846372Sbostic 		}
34946372Sbostic 		old_bufp->flags |= BUF_MOD;
35046372Sbostic 	    } else {
35146372Sbostic 		/* Move to new page */
35246372Sbostic 		if (PAIRFITS(np,(&key),(&val))) putpair((char *)np, &key, &val);
35346372Sbostic 		else {
35446372Sbostic 		    new_bufp = __add_ovflpage ( new_bufp );
35546372Sbostic 		    if ( !new_bufp )return(-1);
35646372Sbostic 		    np = (u_short *)new_bufp->page;
35746372Sbostic 		    putpair ((char *)np, &key, &val);
35846372Sbostic 		}
35946372Sbostic 		new_bufp->flags |= BUF_MOD;
36046372Sbostic 	    }
36146372Sbostic 	}
36246372Sbostic     }
36346372Sbostic     if ( last_bfp ) {
36446372Sbostic 	__free_ovflpage(last_bfp);
36546372Sbostic     }
36646372Sbostic 
36746372Sbostic     return (0);
36846372Sbostic }
36946372Sbostic /*
37046372Sbostic     Add the given pair to the page
37146372Sbostic     1 ==> failure
37246372Sbostic     0 ==> OK
37346372Sbostic */
37446372Sbostic extern int
37546372Sbostic __addel(bufp, key, val)
37646372Sbostic BUFHEAD	*bufp;
37746372Sbostic DBT	*key;
37846372Sbostic DBT	*val;
37946372Sbostic {
38046372Sbostic     register u_short *bp = (u_short *)bufp->page;
38146372Sbostic     register u_short *sop;
38246372Sbostic     int	do_expand;
38346372Sbostic 
38446372Sbostic     do_expand = 0;
38546372Sbostic     while ( bp[0] &&  (bp[bp[0]] < REAL_KEY) ) {
38646372Sbostic 	/* Exception case */
38746372Sbostic 	if ( bp[2] < REAL_KEY ) {
38846372Sbostic 	    /* This is a big-keydata pair */
38946372Sbostic 	    bufp = __add_ovflpage(bufp);
39046372Sbostic 	    if ( !bufp ) {
39146372Sbostic 		return(-1);
39246372Sbostic 	    }
39346372Sbostic 	    bp = (u_short *)bufp->page;
39446372Sbostic 	} else {
39546372Sbostic 	    /* Try to squeeze key on this page */
39646372Sbostic 	    if ( FREESPACE(bp) > PAIRSIZE(key,val) ) {
39746372Sbostic 		squeeze_key ( bp, key, val );
39846372Sbostic 		return(0);
39946372Sbostic 	    } else {
40046372Sbostic 		bufp = __get_buf ( bp[bp[0]-1], bufp, 0 );
40146372Sbostic 		if (!bufp) {
40246372Sbostic 		    return(-1);
40346372Sbostic 		}
40446372Sbostic 		bp = (u_short *)bufp->page;
40546372Sbostic 	    }
40646372Sbostic 	}
40746372Sbostic     }
40846372Sbostic 
40946372Sbostic     if ( PAIRFITS(bp,key,val) ) putpair (bufp->page, key, val);
41046372Sbostic     else {
41146372Sbostic 	do_expand = 1;
41246372Sbostic 	bufp = __add_ovflpage ( bufp );
41346372Sbostic 	if (!bufp)return(-1);
41446372Sbostic 	sop = (u_short *) bufp->page;
41546372Sbostic 
41646372Sbostic 	if ( PAIRFITS(sop, key, val) ) putpair ( (char *)sop, key, val );
41746372Sbostic 	else if ( __big_insert ( bufp, key, val ) ) {
41846372Sbostic 	    return(-1);
41946372Sbostic 	}
42046372Sbostic     }
42146372Sbostic     bufp->flags |= BUF_MOD;
42246372Sbostic     /*
42346372Sbostic 	If the average number of keys per bucket exceeds the fill factor,
42446372Sbostic 	expand the table
42546372Sbostic     */
42646372Sbostic     hashp->NKEYS++;
42746372Sbostic     if (do_expand ||
42846372Sbostic 	(hashp->NKEYS / (hashp->MAX_BUCKET+1) > hashp->FFACTOR) ) {
42946372Sbostic 	 return(__expand_table());
43046372Sbostic     }
43146372Sbostic     return(0);
43246372Sbostic }
43346372Sbostic 
43446372Sbostic /*
43546372Sbostic     returns a pointer, NULL on error
43646372Sbostic */
43746372Sbostic extern BUFHEAD *
43846372Sbostic __add_ovflpage ( bufp )
43946372Sbostic BUFHEAD	*bufp;
44046372Sbostic {
44146372Sbostic     register	u_short	*sp = (u_short *)bufp->page;
44246372Sbostic 
44346372Sbostic     u_short	ovfl_num;
44446372Sbostic     u_short	ndx, newoff;
44546372Sbostic     char	*op;
44646372Sbostic     DBT	okey, oval;
44746372Sbostic #ifdef DEBUG1
44846372Sbostic     int	tmp1, tmp2;
44946372Sbostic #endif
45046372Sbostic 
45146372Sbostic     bufp->flags |= BUF_MOD;
45246372Sbostic     ovfl_num = overflow_page ();
45346372Sbostic #ifdef DEBUG1
45446372Sbostic     tmp1 = bufp->addr;
45546372Sbostic     tmp2 = bufp->ovfl?bufp->ovfl->addr:0;
45646372Sbostic #endif
45746372Sbostic     if (!ovfl_num || !(bufp->ovfl = __get_buf ( ovfl_num, bufp, 1 ))) {
45846372Sbostic 	return(NULL);
45946372Sbostic     }
46046372Sbostic     bufp->ovfl->flags |= BUF_MOD;
46146372Sbostic #ifdef DEBUG1
46246372Sbostic     fprintf ( stderr, "ADDOVFLPAGE: %d->ovfl was %d is now %d\n", tmp1, tmp2,
46346372Sbostic 		bufp->ovfl->addr );
46446372Sbostic #endif
46546372Sbostic     ndx = sp[0];
46646372Sbostic     /*
46746372Sbostic 	Since a pair is allocated on a page only if there's room
46846372Sbostic 	to add an overflow page, we know that the OVFL information
46946372Sbostic 	will fit on the page
47046372Sbostic     */
47146372Sbostic     sp[ndx+4] = OFFSET(sp);
47246372Sbostic     sp[ndx+3] = FREESPACE(sp) - OVFLSIZE;
47346372Sbostic     sp[ndx+1] = ovfl_num;
47446372Sbostic     sp[ndx+2] = OVFLPAGE;
47546372Sbostic     sp[0] = ndx+2;
47646372Sbostic #ifdef HASH_STATISTICS
47746372Sbostic 	    hash_overflows++;
47846372Sbostic #endif
47946372Sbostic     return(bufp->ovfl);
48046372Sbostic }
48146372Sbostic 
48246372Sbostic /*
48346372Sbostic     0 indicates SUCCESS
48446372Sbostic     -1 indicates FAILURE
48546372Sbostic */
48646372Sbostic extern	int
48746372Sbostic __get_page ( p, bucket, is_bucket, is_disk, is_bitmap )
48846372Sbostic char	*p;
48947250Sbostic u_int	bucket;
49046372Sbostic int	is_bucket;
49146372Sbostic int	is_disk;
49246372Sbostic int	is_bitmap;
49346372Sbostic {
49446372Sbostic     register int size;
49546372Sbostic     register int fd;
49646372Sbostic     register int page;
49746372Sbostic     u_short	*bp;
49846372Sbostic     int		rsize;
49946372Sbostic 
50046372Sbostic     fd = hashp->fp;
50146372Sbostic     size = hashp->BSIZE;
50246372Sbostic 
50346502Sbostic     if ( (fd == -1) || !is_disk ) {
50446372Sbostic 	PAGE_INIT(p);
50546372Sbostic 	return(0);
50646372Sbostic     }
50746372Sbostic 
50846372Sbostic     if ( is_bucket) page = BUCKET_TO_PAGE (bucket);
50946372Sbostic     else page = OADDR_TO_PAGE (bucket);
51046500Sbostic     if ((lseek ( fd, page << hashp->BSHIFT, SEEK_SET ) == -1) ||
51146372Sbostic 	((rsize = read ( fd, p, size )) == -1 )) {
51246372Sbostic 	return(-1);
51346372Sbostic     }
51446372Sbostic     bp = (u_short *)p;
51546372Sbostic     if ( !rsize ) {
51646372Sbostic 	bp[0] = 0;		/* We hit the EOF, so initialize a new page */
51746372Sbostic     } else if ( rsize != size ) {
51846372Sbostic 	errno = EFTYPE;
51946372Sbostic 	return(-1);
52046372Sbostic     }
52146372Sbostic     if (!bp[0]) {
52246372Sbostic 	PAGE_INIT(p);
52346372Sbostic     } else if ( hashp->LORDER != BYTE_ORDER ) {
52446372Sbostic 	register int i;
52546372Sbostic 	register int max;
52646372Sbostic 
52746372Sbostic 	if ( is_bitmap ) {
52846372Sbostic 	    max = hashp->BSIZE >> 2;	/* divide by 4 */
52946372Sbostic 	    for ( i=0; i < max; i++ ) {
53046372Sbostic 		BLSWAP(((long *)p)[i]);
53146372Sbostic 	    }
53246372Sbostic 	} else {
53346372Sbostic 	    BSSWAP(bp[0]);
53446372Sbostic 	    max = bp[0] + 2;
53546372Sbostic 	    for ( i=1; i <= max; i++ ) {
53646372Sbostic 		BSSWAP(bp[i]);
53746372Sbostic 	    }
53846372Sbostic 	}
53946372Sbostic     }
54046372Sbostic     return (0);
54146372Sbostic }
54246372Sbostic 
54346372Sbostic /*
54446372Sbostic     Write page p to disk
54546372Sbostic     -1==>failure
54646372Sbostic     0==> OK
54746372Sbostic */
54846372Sbostic extern int
54946372Sbostic __put_page ( p, bucket, is_bucket, is_bitmap )
55046372Sbostic char	*p;
55147250Sbostic u_int	bucket;
55246372Sbostic int	is_bucket;
55346372Sbostic int	is_bitmap;
55446372Sbostic {
55546372Sbostic     register int size;
55646372Sbostic     register int fd;
55746372Sbostic     register int page;
55846372Sbostic     int	wsize;
55946372Sbostic 
56046372Sbostic     size = hashp->BSIZE;
56146460Sbostic     if ( (hashp->fp == -1) && open_temp() ) return (1);
56246372Sbostic     fd = hashp->fp;
56346372Sbostic 
56446372Sbostic     if ( hashp->LORDER != BYTE_ORDER ) {
56546372Sbostic 	register int i;
56646372Sbostic 	register int max;
56746372Sbostic 
56846372Sbostic 	if ( is_bitmap ) {
56946372Sbostic 	    max = hashp->BSIZE >> 2;	/* divide by 4 */
57046372Sbostic 	    for ( i=0; i < max; i++ ) {
57146372Sbostic 		BLSWAP(((long *)p)[i]);
57246372Sbostic 	    }
57346372Sbostic 	} else {
57446372Sbostic 	    max = ((u_short *)p)[0] + 2;
57546372Sbostic 	    for ( i=0; i <= max; i++ ) {
57646372Sbostic 		BSSWAP(((u_short *)p)[i]);
57746372Sbostic 	    }
57846372Sbostic 	}
57946372Sbostic     }
58046372Sbostic     if (is_bucket ) page = BUCKET_TO_PAGE (bucket);
58146372Sbostic     else page = OADDR_TO_PAGE ( bucket );
58246500Sbostic     if ((lseek ( fd, page << hashp->BSHIFT, SEEK_SET ) == -1) ||
58346372Sbostic 	((wsize = write ( fd, p, size )) == -1 )) {
58446372Sbostic 	/* Errno is set */
58546372Sbostic 	return(-1);
58646372Sbostic     }
58746372Sbostic     if ( wsize != size ) {
58846372Sbostic 	errno = EFTYPE;
58946372Sbostic 	return(-1);
59046372Sbostic     }
59146372Sbostic     return(0);
59246372Sbostic }
59346372Sbostic #define BYTE_MASK	((1 << INT_BYTE_SHIFT) -1)
59446372Sbostic /*
59546372Sbostic     Initialize a new bitmap page.  Bitmap pages are left in memory
59646372Sbostic     once they are read in.
59746372Sbostic */
59846372Sbostic extern u_long *
59946372Sbostic __init_bitmap(pnum, nbits, ndx)
60046372Sbostic u_short	pnum;
60146372Sbostic int	nbits;
60246372Sbostic int	ndx;
60346372Sbostic {
60446372Sbostic     u_long	*ip;
60546372Sbostic     int		clearints;
60646372Sbostic     int		clearbytes;
60746372Sbostic 
60846372Sbostic     if ( !(ip = (u_long *)malloc (hashp->BSIZE)) ) return (NULL);
60947250Sbostic     hashp->nmaps++;
61046372Sbostic     clearints = ((nbits - 1) >> INT_BYTE_SHIFT) + 1;
61146372Sbostic     clearbytes = clearints << INT_TO_BYTE;
61246372Sbostic     memset ((char *)ip, 0, clearbytes );
61346372Sbostic     memset ( ((char *) ip) + clearbytes, 0xFF,
61446372Sbostic 		hashp->BSIZE-clearbytes );
61546372Sbostic     ip[clearints-1] = ALL_SET << (nbits & BYTE_MASK);
61646372Sbostic     SETBIT(ip, 0);
61746372Sbostic     hashp->BITMAPS[ndx] = pnum;
61846372Sbostic     hashp->mapp[ndx] = ip;
61946372Sbostic     return(ip);
62046372Sbostic }
62146372Sbostic static int
62246372Sbostic first_free ( map )
62346372Sbostic u_long map;
62446372Sbostic {
62546372Sbostic     register u_long      mask;
62646372Sbostic     register u_long      i;
62746372Sbostic 
62846372Sbostic     mask = 0x1;
62946372Sbostic     for ( i=0; i < BITS_PER_MAP; i++ ) {
63046372Sbostic 	if ( !(mask & map) ) return(i);
63146372Sbostic 	mask = mask << 1;
63246372Sbostic     }
63346372Sbostic     return ( i );
63446372Sbostic }
63546372Sbostic 
63646372Sbostic static u_short
63746372Sbostic overflow_page ( )
63846372Sbostic {
63946372Sbostic     register	int max_free;
64046372Sbostic     register	int splitnum;
64146372Sbostic     register	u_long *freep;
64246372Sbostic     register	int offset;
64346372Sbostic     u_short	addr;
64446372Sbostic     int		in_use_bits;
64546372Sbostic     int		free_page, free_bit;
64646372Sbostic     int		i, j, bit;
64746372Sbostic #ifdef DEBUG2
64846372Sbostic     int	tmp1, tmp2;
64946372Sbostic #endif
65046372Sbostic 
65146372Sbostic     splitnum = __log2(hashp->MAX_BUCKET);
65246372Sbostic     max_free = hashp->SPARES[splitnum];
65346372Sbostic 
65446372Sbostic     free_page = (max_free-1) >> (hashp->BSHIFT + BYTE_SHIFT);
65546372Sbostic     free_bit  = (max_free-1) & ((hashp->BSIZE << BYTE_SHIFT) - 1);
65646372Sbostic 
65746372Sbostic     /* Look through all the free maps to find the first free block */
65846372Sbostic     for ( i = 0; i <= free_page; i++ ) {
65947250Sbostic 	if (!(freep = (u_long *)hashp->mapp[i])  &&
66047250Sbostic 	    !(freep = fetch_bitmap(i)) ) {
66147250Sbostic 	    return ( NULL );
66247250Sbostic 	}
66346372Sbostic 	if ( i == free_page ) in_use_bits = free_bit;
66446372Sbostic 	else in_use_bits = (hashp->BSIZE << BYTE_SHIFT) -1;
66546372Sbostic 
66646372Sbostic 	for (j = 0, bit = 0; bit <= in_use_bits; j++, bit += BITS_PER_MAP ) {
66746372Sbostic 	     if ( freep[j] != ALL_SET ) goto found;
66846372Sbostic 	}
66946372Sbostic     }
67046372Sbostic     /* No Free Page Found */
67146372Sbostic     hashp->SPARES[splitnum]++;
67246372Sbostic     offset = hashp->SPARES[splitnum] -
67346372Sbostic 	     (splitnum ? hashp->SPARES[splitnum-1] : 0);
67446372Sbostic 
67546372Sbostic     /* Check if we need to allocate a new bitmap page */
67646372Sbostic     if ( free_bit == (hashp->BSIZE << BYTE_SHIFT) - 1 ) {
67746372Sbostic 	free_page++;
678*50058Sbostic #define	OVMSG	"hash: out of overflow pages; increase page size\n"
67946372Sbostic 	if ( free_page >= NCACHED ) {
680*50058Sbostic 	    (void) write (STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1);
68146372Sbostic 	    return(NULL);
68246372Sbostic 	}
68346372Sbostic 	/*
68446372Sbostic 	    This is tricky.  The 1 indicates that you want the
68546372Sbostic 	    new page allocated with 1 clear bit.  Actually, you
68646372Sbostic 	    are going to allocate 2 pages from this map.  The first
68746372Sbostic 	    is going to be the map page, the second is the overflow
68846372Sbostic 	    page we were looking for.  The init_bitmap routine
68946372Sbostic 	    automatically, sets the first bit of itself to indicate
69046372Sbostic 	    that the bitmap itself is in use.  We would explicitly
69146372Sbostic 	    set the second bit, but don't have to if we tell init_bitmap
69246372Sbostic 	    not to leave it clear in the first place.
69346372Sbostic 	*/
69446372Sbostic 	__init_bitmap ( OADDR_OF(splitnum, offset), 1, free_page );
69546372Sbostic 	hashp->SPARES[splitnum]++;
69646372Sbostic #ifdef DEBUG2
69746372Sbostic 	free_bit = 2;
69846372Sbostic #endif
69946372Sbostic 	offset++;
70046372Sbostic     } else {
70146372Sbostic 	/*
70246372Sbostic 		Free_bit addresses the last used bit.  Bump it to
70346372Sbostic 		address the first available bit.
70446372Sbostic 	*/
70546372Sbostic 	free_bit++;
70646372Sbostic 	SETBIT ( freep, free_bit );
70746372Sbostic     }
70846372Sbostic 
70946372Sbostic     /* Calculate address of the new overflow page */
71046372Sbostic     if ( offset > SPLITMASK ) {
711*50058Sbostic 	(void) write (STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1);
71246372Sbostic 	return(NULL);
71346372Sbostic     }
71446372Sbostic     addr = OADDR_OF(splitnum, offset);
71546372Sbostic #ifdef DEBUG2
71646372Sbostic     fprintf ( stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n",
71746372Sbostic 		addr, free_bit, free_page );
71846372Sbostic #endif
71946372Sbostic     return(addr);
72046372Sbostic 
72146372Sbostic found:
72246372Sbostic     bit = bit + first_free(freep[j]);
72346372Sbostic     SETBIT(freep,bit);
72446372Sbostic #ifdef DEBUG2
72546372Sbostic     tmp1 = bit;
72646372Sbostic     tmp2 = i;
72746372Sbostic #endif
72846372Sbostic     /*
72946372Sbostic 	Bits are addressed starting with 0, but overflow pages are
73046372Sbostic 	addressed beginning at 1. Bit is a bit addressnumber, so we
73146372Sbostic 	need to increment it to convert it to a page number.
73246372Sbostic     */
73346372Sbostic     bit = 1 + bit + (i * (hashp->BSIZE << BYTE_SHIFT));
73446372Sbostic 
73546372Sbostic     /* Calculate the split number for this page */
73646372Sbostic     for ( i = 0; (i < splitnum) && (bit > hashp->SPARES[i]); i++ );
73746372Sbostic     offset =(i ? bit - hashp->SPARES[i-1] : bit );
73846372Sbostic     if ( offset >= SPLITMASK ) return(NULL);/* Out of overflow pages */
73946372Sbostic     addr = OADDR_OF(i, offset);
74046372Sbostic #ifdef DEBUG2
74146372Sbostic     fprintf ( stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n",
74246372Sbostic 		addr, tmp1, tmp2 );
74346372Sbostic #endif
74446372Sbostic 
74546372Sbostic     /* Allocate and return the overflow page */
74646372Sbostic     return (addr);
74746372Sbostic }
74846372Sbostic 
74946372Sbostic /*
75046372Sbostic     Mark this overflow page as free.
75146372Sbostic */
75246372Sbostic __free_ovflpage ( obufp )
75346372Sbostic BUFHEAD	*obufp;
75446372Sbostic {
75546372Sbostic     register u_short addr = obufp->addr;
75646372Sbostic     int	free_page, free_bit;
75746372Sbostic     int bit_address;
75846372Sbostic     u_short ndx;
75946372Sbostic     u_long *freep;
76046372Sbostic     int j;
76146372Sbostic 
76246372Sbostic #ifdef DEBUG1
76346372Sbostic     fprintf ( stderr, "Freeing %d\n", addr );
76446372Sbostic #endif
76546372Sbostic     ndx = (((u_short)addr) >> SPLITSHIFT);
76646372Sbostic     bit_address = (ndx ? hashp->SPARES[ndx-1] : 0) + (addr & SPLITMASK) - 1;
76746372Sbostic     free_page = (bit_address >> (hashp->BSHIFT + BYTE_SHIFT));
76846372Sbostic     free_bit  = bit_address & ((hashp->BSIZE << BYTE_SHIFT) - 1);
76946372Sbostic 
77047250Sbostic     if ( !(freep = hashp->mapp[free_page]) &&
77147250Sbostic 	 !(freep = fetch_bitmap( free_page )) ) {
77247250Sbostic 	/*
77347250Sbostic 	    This had better never happen.  It means we tried to
77447250Sbostic 	    read a bitmap that has already had overflow pages allocated
77547250Sbostic 	    off it, and we failed to read it from the file
77647250Sbostic 	*/
77747250Sbostic 	assert(0);
77847250Sbostic     }
77946372Sbostic     CLRBIT(freep, free_bit);
78046372Sbostic #ifdef DEBUG2
78146372Sbostic     fprintf ( stderr, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n",
78246372Sbostic 		obufp->addr, free_bit, free_page );
78346372Sbostic #endif
78446372Sbostic     __reclaim_buf ( obufp );
78546372Sbostic     return;
78646372Sbostic }
78746372Sbostic 
78846372Sbostic /*
78946372Sbostic 0 success
79046372Sbostic -1 failure
79146372Sbostic */
79246372Sbostic static int
79346372Sbostic open_temp()
79446372Sbostic {
79546416Sbostic     sigset_t	set, oset;
79646742Sbostic     static char	namestr[] = "_hashXXXXXX";
79746372Sbostic 
79846416Sbostic     /* Block signals; make sure file goes away at process exit. */
79946416Sbostic     sigemptyset(&set);
80046416Sbostic     sigaddset(&set, SIGHUP);
80146416Sbostic     sigaddset(&set, SIGINT);
80246416Sbostic     sigaddset(&set, SIGQUIT);
80346416Sbostic     sigaddset(&set, SIGTERM);
80446416Sbostic     (void)sigprocmask(SIG_BLOCK, &set, &oset);
80546416Sbostic     if ((hashp->fp = mkstemp ( namestr )) != -1) {
80646416Sbostic 	(void)unlink(namestr);
80746416Sbostic 	(void)fcntl(hashp->fp, F_SETFD, 1);
80846372Sbostic     }
80946416Sbostic     (void)sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
81046416Sbostic     return(hashp->fp != -1 ? 0 : -1);
81146372Sbostic }
81246372Sbostic 
81346372Sbostic /*
81446372Sbostic     We have to know that the key will fit, but the
81546372Sbostic     last entry on the page is an overflow pair, so we
81646372Sbostic     need to shift things.
81746372Sbostic */
81846372Sbostic static void
81946372Sbostic squeeze_key ( sp, key, val )
82046372Sbostic u_short	*sp;
82146372Sbostic DBT	*key;
82246372Sbostic DBT	*val;
82346372Sbostic {
82446372Sbostic     register	char	*p = (char *)sp;
82546372Sbostic     u_short	free_space, off;
82646372Sbostic     u_short	pageno, n;
82746372Sbostic 
82846372Sbostic     n = sp[0];
82946372Sbostic     free_space = FREESPACE(sp);
83046372Sbostic     off = OFFSET(sp);
83146372Sbostic 
83246372Sbostic     pageno = sp[n-1];
83346372Sbostic     off -= key->size;
83446372Sbostic     sp[n-1] = off;
83546372Sbostic     bcopy ( key->data, p + off, key->size );
83646372Sbostic     off -= val->size;
83746372Sbostic     sp[n] = off;
83846372Sbostic     bcopy ( val->data, p + off, val->size );
83946372Sbostic     sp[0] = n+2;
84046372Sbostic     sp[n+1] = pageno;
84146372Sbostic     sp[n+2] = OVFLPAGE;
84246372Sbostic     FREESPACE(sp) = free_space - PAIRSIZE(key,val);
84346372Sbostic     OFFSET(sp) = off;
84446372Sbostic }
84546372Sbostic 
84647250Sbostic static u_long *
84747250Sbostic fetch_bitmap ( ndx )
84847250Sbostic int	ndx;
84947250Sbostic {
85047250Sbostic     if ( ndx >= hashp->nmaps  ||
85147250Sbostic 	!(hashp->mapp[ndx] = (u_long *)malloc ( hashp->BSIZE )) ||
85247250Sbostic 	 __get_page ((char *)hashp->mapp[ndx], hashp->BITMAPS[ndx], 0, 1, 1)) {
85347250Sbostic 
85447250Sbostic 	    return(NULL);
85547250Sbostic     }
85647250Sbostic     return ( hashp->mapp[ndx] );
85747250Sbostic }
85846372Sbostic #ifdef DEBUG4
85946372Sbostic print_chain ( addr )
86046372Sbostic short	addr;
86146372Sbostic {
86246372Sbostic 	BUFHEAD	*bufp;
86346372Sbostic 	short	*bp;
86446372Sbostic 	short	oaddr;
86546372Sbostic 
86646372Sbostic 	fprintf ( stderr, "%d ", addr );
86746372Sbostic 	bufp = __get_buf ( (int)addr, NULL, 0 );
86846372Sbostic 	bp = (short *)bufp->page;
86946372Sbostic 	while ( bp[0] &&
87046372Sbostic 		((bp[bp[0]] == OVFLPAGE) ||
87146372Sbostic 		 ((bp[0] > 2) && bp[2] < REAL_KEY))) {
87246372Sbostic 		oaddr = bp[bp[0]-1];
87346372Sbostic 		fprintf ( stderr, "%d ", (int)oaddr );
87446372Sbostic 		bufp = __get_buf ( (int)oaddr, bufp, 0 );
87546372Sbostic 		bp = (short *)bufp->page;
87646372Sbostic 	}
87746372Sbostic 	fprintf ( stderr, "\n" );
87846372Sbostic }
87946372Sbostic #endif
880