xref: /csrg-svn/lib/libc/db/hash/hash_page.c (revision 46562)
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*46562Sbostic static char sccsid[] = "@(#)hash_page.c	5.9 (Berkeley) 02/22/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>
31*46562Sbostic #include <fcntl.h>
3246416Sbostic #include <signal.h>
3346372Sbostic #include <assert.h>
3446372Sbostic #include <errno.h>
3546372Sbostic #include <db.h>
3646372Sbostic #include <stdio.h>
37*46562Sbostic #include <stdlib.h>
38*46562Sbostic #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 */
5546372Sbostic extern	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();
7046372Sbostic 
7146372Sbostic #ifdef HASH_STATISTICS
7246372Sbostic extern long hash_accesses, hash_collisions, hash_expansions, hash_overflows;
7346372Sbostic #endif
7446372Sbostic #define	PAGE_INIT(P)					\
7546372Sbostic {							\
7646372Sbostic     ((u_short *)P)[0] = 0;				\
7746372Sbostic     ((u_short *)P)[1] = hashp->BSIZE - 3 * sizeof(u_short);	\
7846372Sbostic     ((u_short *)P)[2] = hashp->BSIZE;			\
7946372Sbostic }
8046372Sbostic 
8146372Sbostic /*
8246372Sbostic 	This is called AFTER we have verified that there is room on the
8346372Sbostic 	page for the pair (PAIRFITS has returned true) so we go right
8446372Sbostic 	ahead and start moving stuff on.
8546372Sbostic */
8646372Sbostic static void
8746372Sbostic putpair(p, key, val)
8846372Sbostic char *p;
8946372Sbostic DBT *key;
9046372Sbostic DBT *val;
9146372Sbostic {
9246372Sbostic 	register u_short n;
9346372Sbostic 	register u_short off;
9446372Sbostic 	register u_short *bp = (u_short *) p;
9546372Sbostic 
9646372Sbostic /* enter the key first */
9746372Sbostic 	n = bp[0];
9846372Sbostic 
9946372Sbostic 	off = OFFSET(bp) - key->size;
10046372Sbostic 	bcopy( key->data, p+off, key->size );
10146372Sbostic 	bp[++n] = off;
10246372Sbostic 
10346372Sbostic /* now the data */
10446372Sbostic 	off -= val->size;
10546372Sbostic 	bcopy(val->data,  p + off, val->size);
10646372Sbostic 	bp[++n] = off;
10746372Sbostic 
10846372Sbostic /* adjust page info */
10946372Sbostic 	bp[0] = n;
11046372Sbostic 	bp[n+1] = off - ((n+3)*sizeof(u_short));
11146372Sbostic 	bp[n+2] = off;
11246372Sbostic 	return;
11346372Sbostic }
11446372Sbostic /*
11546372Sbostic     0 OK
11646372Sbostic     -1 error
11746372Sbostic */
11846372Sbostic extern int
11946372Sbostic __delpair(bufp, ndx)
12046372Sbostic BUFHEAD *bufp;
12146372Sbostic register int ndx;
12246372Sbostic {
12346372Sbostic 	register u_short *bp = (u_short *) bufp->page;
12446372Sbostic 	register int n = bp[0];
12546372Sbostic 	register u_short newoff;
12646372Sbostic 	u_short pairlen;
12746372Sbostic 
12846372Sbostic 	if ( bp[ndx+1] < REAL_KEY ) return ( __big_delete ( bufp, ndx ) );
12946372Sbostic 	if ( ndx != 1 ) newoff = bp[ndx-1];
13046372Sbostic 	else newoff = hashp->BSIZE;
13146372Sbostic 	pairlen = newoff - bp[ndx+1];
13246372Sbostic 
13346372Sbostic 	if ( ndx != (n-1) ) {
13446372Sbostic 		/* Hard Case -- need to shuffle keys */
13546372Sbostic 		register int i;
13646372Sbostic 		register char *src = bufp->page + (int)OFFSET(bp);
13746372Sbostic 		register char *dst = src + (int)pairlen;
13846372Sbostic 		bcopy ( src, dst, bp[ndx+1] - OFFSET(bp) );
13946372Sbostic 
14046372Sbostic 		/* Now adjust the pointers */
14146372Sbostic 		for ( i = ndx+2; i <= n; i += 2 ) {
14246372Sbostic 		    if ( bp[i+1]  == OVFLPAGE ) {
14346372Sbostic 			bp[i-2] = bp[i];
14446372Sbostic 			bp[i-1] = bp[i+1];
14546372Sbostic 		    } else {
14646372Sbostic 			bp[i-2] = bp[i] + pairlen;
14746372Sbostic 			bp[i-1] = bp[i+1] + pairlen;
14846372Sbostic 		    }
14946372Sbostic 		}
15046372Sbostic 	}
15146372Sbostic 
15246372Sbostic 	/* Finally adjust the page data */
15346372Sbostic 	bp[n] = OFFSET(bp) + pairlen;
15446372Sbostic 	bp[n-1] = bp[n+1] + pairlen + 2 * sizeof(u_short);
15546372Sbostic 	bp[0] = n-2;
15646372Sbostic 	hashp->NKEYS--;
15746372Sbostic 
15846372Sbostic 	bufp->flags |= BUF_MOD;
15946372Sbostic 	return (0);
16046372Sbostic }
16146372Sbostic /*
16246372Sbostic     -1 ==> Error
16346372Sbostic     0 ==> OK
16446372Sbostic */
16546372Sbostic extern int
16646372Sbostic __split_page(obucket, nbucket)
16746372Sbostic int obucket;
16846372Sbostic int nbucket;
16946372Sbostic {
17046372Sbostic 	DBT key;
17146372Sbostic 	DBT val;
17246372Sbostic 
17346372Sbostic 	register BUFHEAD *new_bufp;
17446372Sbostic 	register BUFHEAD *old_bufp;
17546372Sbostic 	register u_short *ino;
17646372Sbostic 	register char	*np;
17746460Sbostic 	int n;
17846460Sbostic 	int ndx;
17946460Sbostic 	int retval;
18046372Sbostic 	char	*op;
18146372Sbostic 
18246372Sbostic 	u_short copyto = (u_short)hashp->BSIZE;
18346460Sbostic 	u_short diff;
18446372Sbostic 	u_short off = (u_short)hashp->BSIZE;
18546372Sbostic 	u_short moved;
18646372Sbostic 
18746372Sbostic 	old_bufp = __get_buf ( obucket, NULL, 0 );
18846372Sbostic 	new_bufp = __get_buf ( nbucket, NULL, 0 );
18946372Sbostic 
19046460Sbostic 	old_bufp->flags |= (BUF_MOD|BUF_PIN);
19146460Sbostic 	new_bufp->flags |= (BUF_MOD|BUF_PIN);
19246372Sbostic 
19346372Sbostic 	ino = (u_short *)(op = old_bufp->page);
19446372Sbostic 	np = new_bufp->page;
19546372Sbostic 
19646372Sbostic 	moved = 0;
19746372Sbostic 
19846372Sbostic 	for (n = 1, ndx = 1; n < ino[0]; n+=2) {
19946372Sbostic 		if ( ino[n+1] < REAL_KEY ) {
20046460Sbostic 		    retval = ugly_split( obucket, old_bufp, new_bufp,
20146460Sbostic 					 copyto, moved );
20246460Sbostic 		    old_bufp->flags &= ~BUF_PIN;
20346460Sbostic 		    new_bufp->flags &= ~BUF_PIN;
20446460Sbostic 		    return(retval);
20546460Sbostic 
20646372Sbostic 		}
20746372Sbostic 		key.data = op + ino[n];
20846372Sbostic 		key.size = off - ino[n];
20946372Sbostic 
21046372Sbostic 		if ( __call_hash ( key.data, key.size ) == obucket ) {
21146372Sbostic 		    /* Don't switch page */
21246372Sbostic 		    diff = copyto - off;
21346372Sbostic 		    if ( diff ) {
21446372Sbostic 			copyto = ino[n+1] + diff;
21546372Sbostic 			bcopy ( op + ino[n+1], op + copyto,  off-ino[n+1]);
21646372Sbostic 			ino[ndx] = copyto + ino[n] - ino[n+1];
21746372Sbostic 			ino[ndx+1] = copyto;
21846372Sbostic 		    } else copyto = ino[n+1];
21946372Sbostic 		    ndx += 2;
22046372Sbostic 		} else {
22146372Sbostic 		    /* Switch page */
22246372Sbostic 		    val.data = op + ino[n+1];
22346372Sbostic 		    val.size = ino[n] - ino[n+1];
22446372Sbostic 		    putpair( np, &key, &val);
22546372Sbostic 		    moved +=2;
22646372Sbostic 		}
22746372Sbostic 
22846372Sbostic 		off = ino[n+1];
22946372Sbostic 	}
23046372Sbostic 
23146372Sbostic 	/* Now clean up the page */
23246372Sbostic 	ino[0] -= moved;
23346372Sbostic 	FREESPACE(ino) = copyto - sizeof(u_short) * (ino[0]+3);
23446372Sbostic 	OFFSET(ino) = copyto;
23546372Sbostic 
23646372Sbostic #ifdef DEBUG3
23746372Sbostic 	fprintf(stderr, "split %d/%d\n",
23846372Sbostic 	       ((u_short *) np)[0] / 2,
23946372Sbostic 	       ((u_short *) op)[0] / 2);
24046372Sbostic #endif
24146460Sbostic 	/* unpin both pages */
24246460Sbostic 	old_bufp->flags &= ~BUF_PIN;
24346460Sbostic 	new_bufp->flags &= ~BUF_PIN;
24446372Sbostic 	return(0);
24546372Sbostic }
24646372Sbostic /*
24746372Sbostic     0 ==> success
24846372Sbostic     -1 ==> failure
24946372Sbostic 
25046460Sbostic     Called when we encounter an overflow or big key/data page during
25146460Sbostic     split handling.
25246460Sbostic     This is special cased since we have to begin checking whether
25346372Sbostic     the key/data pairs fit on their respective pages and because
25446372Sbostic     we may need overflow pages for both the old and new pages
25546460Sbostic 
25646460Sbostic     The first page might be a page with regular key/data pairs
25746460Sbostic     in which case we have a regular overflow condition and just
25846460Sbostic     need to go on to the next page or it might be a big key/data
25946460Sbostic     pair in which case we need to fix the big key/data pair.
26046372Sbostic */
26146372Sbostic static int
26246372Sbostic ugly_split( obucket, old_bufp, new_bufp, copyto, moved )
26346372Sbostic int	obucket;	/* Same as __split_page */
26446372Sbostic BUFHEAD	*old_bufp;
26546372Sbostic BUFHEAD	*new_bufp;
26646372Sbostic u_short	copyto;		/* First byte on page which contains key/data values */
26746372Sbostic int	moved;		/* number of pairs moved to new page */
26846372Sbostic {
26946372Sbostic     register BUFHEAD *bufp = old_bufp;		/* Buffer header for ino */
27046372Sbostic     register u_short	*ino = (u_short *)old_bufp->page;
27146372Sbostic 						/* Page keys come off of */
27246372Sbostic     register u_short	*np = (u_short *)new_bufp->page;	/* New page */
27346372Sbostic     register u_short	*op = (u_short *)old_bufp->page;
27446372Sbostic 						/* Page keys go on to if they
27546372Sbostic 						   aren't moving */
27646372Sbostic 
27746372Sbostic     char	*cino;				/* Character value of ino */
27846372Sbostic     BUFHEAD	*last_bfp = NULL;		/* Last buffer header OVFL which
27946372Sbostic 						   needs to be freed */
28046372Sbostic     u_short	ov_addr, last_addr = 0;
28146372Sbostic     u_short	n;
28246372Sbostic     u_short	off;
28346372Sbostic 
28446372Sbostic     DBT	key, val;
28546372Sbostic     SPLIT_RETURN	ret;
28646372Sbostic 
28746372Sbostic     n = ino[0]-1;
28846372Sbostic     while ( n < ino[0] ) {
28946502Sbostic 	if ( ino[2] < REAL_KEY && ino[2] != OVFLPAGE ) {
29046502Sbostic 	    if (__big_split (old_bufp, new_bufp, bufp, ov_addr, obucket, &ret)) {
29146502Sbostic 		return(-1);
29246502Sbostic 	    }
29346502Sbostic 	    old_bufp = ret.oldp;
29446502Sbostic 	    if ( !old_bufp ) return(-1);
29546502Sbostic 	    op = (u_short *)old_bufp->page;
29646502Sbostic 	    new_bufp = ret.newp;
29746502Sbostic 	    if ( !new_bufp ) return(-1);
29846502Sbostic 	    np = (u_short *)new_bufp->page;
29946502Sbostic 	    bufp = ret.nextp;
30046502Sbostic 	    if ( !bufp ) return(0);
30146502Sbostic 	    cino = (char *)bufp->page;
30246502Sbostic 	    ino = (u_short *)cino;
30346502Sbostic 	    last_bfp = ret.nextp;
30446502Sbostic 	} else if ( ino[n+1] == OVFLPAGE ) {
30546372Sbostic 	    ov_addr = ino[n];
30646372Sbostic 	    /*
30746372Sbostic 		Fix up the old page -- the extra 2 are the fields which
30846372Sbostic 		contained the overflow information
30946372Sbostic 	    */
31046372Sbostic 	    ino[0] -= (moved + 2);
31146372Sbostic 	    FREESPACE(ino) = copyto - sizeof(u_short) * (ino[0]+3);
31246372Sbostic 	    OFFSET(ino) = copyto;
31346372Sbostic 
31446372Sbostic 	    bufp = __get_buf ( ov_addr, bufp, 0 );
31546372Sbostic 	    if ( !bufp ) return(-1);
31646372Sbostic 
31746372Sbostic 	    ino = (u_short *)bufp->page;
31846372Sbostic 	    n = 1;
31946372Sbostic 	    copyto = hashp->BSIZE;
32046372Sbostic 	    moved = 0;
32146372Sbostic 
32246372Sbostic 	    if ( last_bfp ) {
32346372Sbostic 		__free_ovflpage( last_bfp);
32446372Sbostic 	    }
32546372Sbostic 	    last_bfp = bufp;
32646372Sbostic 	}
32746372Sbostic 
32846372Sbostic 
32946460Sbostic 	/* Move regular sized pairs of there are any */
33046372Sbostic 	off = hashp->BSIZE;
33146372Sbostic 	for ( n = 1; (n < ino[0]) && (ino[n+1] >= REAL_KEY); n += 2 ) {
33246372Sbostic 	    cino = (char *)ino;
33346372Sbostic 	    key.data = cino + ino[n];
33446372Sbostic 	    key.size = off - ino[n];
33546372Sbostic 	    val.data = cino + ino[n+1];
33646372Sbostic 	    val.size = ino[n] - ino[n+1];
33746372Sbostic 	    off = ino[n+1];
33846372Sbostic 
33946372Sbostic 	    if ( __call_hash ( key.data, key.size ) == obucket ) {
34046372Sbostic 		/* Keep on old page */
34146372Sbostic 		if (PAIRFITS(op,(&key),(&val))) putpair((char *)op, &key, &val);
34246372Sbostic 		else {
34346372Sbostic 		    old_bufp = __add_ovflpage ( old_bufp );
34446372Sbostic 		    if ( !old_bufp ) return(-1);
34546372Sbostic 		    op = (u_short *)old_bufp->page;
34646372Sbostic 		    putpair ((char *)op, &key, &val);
34746372Sbostic 		}
34846372Sbostic 		old_bufp->flags |= BUF_MOD;
34946372Sbostic 	    } else {
35046372Sbostic 		/* Move to new page */
35146372Sbostic 		if (PAIRFITS(np,(&key),(&val))) putpair((char *)np, &key, &val);
35246372Sbostic 		else {
35346372Sbostic 		    new_bufp = __add_ovflpage ( new_bufp );
35446372Sbostic 		    if ( !new_bufp )return(-1);
35546372Sbostic 		    np = (u_short *)new_bufp->page;
35646372Sbostic 		    putpair ((char *)np, &key, &val);
35746372Sbostic 		}
35846372Sbostic 		new_bufp->flags |= BUF_MOD;
35946372Sbostic 	    }
36046372Sbostic 	}
36146372Sbostic     }
36246372Sbostic     if ( last_bfp ) {
36346372Sbostic 	__free_ovflpage(last_bfp);
36446372Sbostic     }
36546372Sbostic 
36646372Sbostic     return (0);
36746372Sbostic }
36846372Sbostic /*
36946372Sbostic     Add the given pair to the page
37046372Sbostic     1 ==> failure
37146372Sbostic     0 ==> OK
37246372Sbostic */
37346372Sbostic extern int
37446372Sbostic __addel(bufp, key, val)
37546372Sbostic BUFHEAD	*bufp;
37646372Sbostic DBT	*key;
37746372Sbostic DBT	*val;
37846372Sbostic {
37946372Sbostic     register u_short *bp = (u_short *)bufp->page;
38046372Sbostic     register u_short *sop;
38146372Sbostic     int	do_expand;
38246372Sbostic 
38346372Sbostic     do_expand = 0;
38446372Sbostic     while ( bp[0] &&  (bp[bp[0]] < REAL_KEY) ) {
38546372Sbostic 	/* Exception case */
38646372Sbostic 	if ( bp[2] < REAL_KEY ) {
38746372Sbostic 	    /* This is a big-keydata pair */
38846372Sbostic 	    bufp = __add_ovflpage(bufp);
38946372Sbostic 	    if ( !bufp ) {
39046372Sbostic 		return(-1);
39146372Sbostic 	    }
39246372Sbostic 	    bp = (u_short *)bufp->page;
39346372Sbostic 	} else {
39446372Sbostic 	    /* Try to squeeze key on this page */
39546372Sbostic 	    if ( FREESPACE(bp) > PAIRSIZE(key,val) ) {
39646372Sbostic 		squeeze_key ( bp, key, val );
39746372Sbostic 		return(0);
39846372Sbostic 	    } else {
39946372Sbostic 		bufp = __get_buf ( bp[bp[0]-1], bufp, 0 );
40046372Sbostic 		if (!bufp) {
40146372Sbostic 		    return(-1);
40246372Sbostic 		}
40346372Sbostic 		bp = (u_short *)bufp->page;
40446372Sbostic 	    }
40546372Sbostic 	}
40646372Sbostic     }
40746372Sbostic 
40846372Sbostic     if ( PAIRFITS(bp,key,val) ) putpair (bufp->page, key, val);
40946372Sbostic     else {
41046372Sbostic 	do_expand = 1;
41146372Sbostic 	bufp = __add_ovflpage ( bufp );
41246372Sbostic 	if (!bufp)return(-1);
41346372Sbostic 	sop = (u_short *) bufp->page;
41446372Sbostic 
41546372Sbostic 	if ( PAIRFITS(sop, key, val) ) putpair ( (char *)sop, key, val );
41646372Sbostic 	else if ( __big_insert ( bufp, key, val ) ) {
41746372Sbostic 	    return(-1);
41846372Sbostic 	}
41946372Sbostic     }
42046372Sbostic     bufp->flags |= BUF_MOD;
42146372Sbostic     /*
42246372Sbostic 	If the average number of keys per bucket exceeds the fill factor,
42346372Sbostic 	expand the table
42446372Sbostic     */
42546372Sbostic     hashp->NKEYS++;
42646372Sbostic     if (do_expand ||
42746372Sbostic 	(hashp->NKEYS / (hashp->MAX_BUCKET+1) > hashp->FFACTOR) ) {
42846372Sbostic 	 return(__expand_table());
42946372Sbostic     }
43046372Sbostic     return(0);
43146372Sbostic }
43246372Sbostic 
43346372Sbostic /*
43446372Sbostic     returns a pointer, NULL on error
43546372Sbostic */
43646372Sbostic extern BUFHEAD *
43746372Sbostic __add_ovflpage ( bufp )
43846372Sbostic BUFHEAD	*bufp;
43946372Sbostic {
44046372Sbostic     register	u_short	*sp = (u_short *)bufp->page;
44146372Sbostic 
44246372Sbostic     u_short	ovfl_num;
44346372Sbostic     u_short	ndx, newoff;
44446372Sbostic     char	*op;
44546372Sbostic     DBT	okey, oval;
44646372Sbostic #ifdef DEBUG1
44746372Sbostic     int	tmp1, tmp2;
44846372Sbostic #endif
44946372Sbostic 
45046372Sbostic     bufp->flags |= BUF_MOD;
45146372Sbostic     ovfl_num = overflow_page ();
45246372Sbostic #ifdef DEBUG1
45346372Sbostic     tmp1 = bufp->addr;
45446372Sbostic     tmp2 = bufp->ovfl?bufp->ovfl->addr:0;
45546372Sbostic #endif
45646372Sbostic     if (!ovfl_num || !(bufp->ovfl = __get_buf ( ovfl_num, bufp, 1 ))) {
45746372Sbostic 	return(NULL);
45846372Sbostic     }
45946372Sbostic     bufp->ovfl->flags |= BUF_MOD;
46046372Sbostic #ifdef DEBUG1
46146372Sbostic     fprintf ( stderr, "ADDOVFLPAGE: %d->ovfl was %d is now %d\n", tmp1, tmp2,
46246372Sbostic 		bufp->ovfl->addr );
46346372Sbostic #endif
46446372Sbostic     ndx = sp[0];
46546372Sbostic     /*
46646372Sbostic 	Since a pair is allocated on a page only if there's room
46746372Sbostic 	to add an overflow page, we know that the OVFL information
46846372Sbostic 	will fit on the page
46946372Sbostic     */
47046372Sbostic     sp[ndx+4] = OFFSET(sp);
47146372Sbostic     sp[ndx+3] = FREESPACE(sp) - OVFLSIZE;
47246372Sbostic     sp[ndx+1] = ovfl_num;
47346372Sbostic     sp[ndx+2] = OVFLPAGE;
47446372Sbostic     sp[0] = ndx+2;
47546372Sbostic #ifdef HASH_STATISTICS
47646372Sbostic 	    hash_overflows++;
47746372Sbostic #endif
47846372Sbostic     return(bufp->ovfl);
47946372Sbostic }
48046372Sbostic 
48146372Sbostic /*
48246372Sbostic     0 indicates SUCCESS
48346372Sbostic     -1 indicates FAILURE
48446372Sbostic */
48546372Sbostic extern	int
48646372Sbostic __get_page ( p, bucket, is_bucket, is_disk, is_bitmap )
48746372Sbostic char	*p;
48846372Sbostic int	bucket;
48946372Sbostic int	is_bucket;
49046372Sbostic int	is_disk;
49146372Sbostic int	is_bitmap;
49246372Sbostic {
49346372Sbostic     register int size;
49446372Sbostic     register int fd;
49546372Sbostic     register int page;
49646372Sbostic     u_short	*bp;
49746372Sbostic     int		rsize;
49846372Sbostic 
49946372Sbostic     fd = hashp->fp;
50046372Sbostic     size = hashp->BSIZE;
50146372Sbostic 
50246502Sbostic     if ( (fd == -1) || !is_disk ) {
50346372Sbostic 	PAGE_INIT(p);
50446372Sbostic 	return(0);
50546372Sbostic     }
50646372Sbostic 
50746372Sbostic     if ( is_bucket) page = BUCKET_TO_PAGE (bucket);
50846372Sbostic     else page = OADDR_TO_PAGE (bucket);
50946500Sbostic     if ((lseek ( fd, page << hashp->BSHIFT, SEEK_SET ) == -1) ||
51046372Sbostic 	((rsize = read ( fd, p, size )) == -1 )) {
51146372Sbostic 	return(-1);
51246372Sbostic     }
51346372Sbostic     bp = (u_short *)p;
51446372Sbostic     if ( !rsize ) {
51546372Sbostic 	bp[0] = 0;		/* We hit the EOF, so initialize a new page */
51646372Sbostic     } else if ( rsize != size ) {
51746372Sbostic 	errno = EFTYPE;
51846372Sbostic 	return(-1);
51946372Sbostic     }
52046372Sbostic     if (!bp[0]) {
52146372Sbostic 	PAGE_INIT(p);
52246372Sbostic     } else if ( hashp->LORDER != BYTE_ORDER ) {
52346372Sbostic 	register int i;
52446372Sbostic 	register int max;
52546372Sbostic 
52646372Sbostic 	if ( is_bitmap ) {
52746372Sbostic 	    max = hashp->BSIZE >> 2;	/* divide by 4 */
52846372Sbostic 	    for ( i=0; i < max; i++ ) {
52946372Sbostic 		BLSWAP(((long *)p)[i]);
53046372Sbostic 	    }
53146372Sbostic 	} else {
53246372Sbostic 	    BSSWAP(bp[0]);
53346372Sbostic 	    max = bp[0] + 2;
53446372Sbostic 	    for ( i=1; i <= max; i++ ) {
53546372Sbostic 		BSSWAP(bp[i]);
53646372Sbostic 	    }
53746372Sbostic 	}
53846372Sbostic     }
53946372Sbostic     return (0);
54046372Sbostic }
54146372Sbostic 
54246372Sbostic /*
54346372Sbostic     Write page p to disk
54446372Sbostic     -1==>failure
54546372Sbostic     0==> OK
54646372Sbostic */
54746372Sbostic extern int
54846372Sbostic __put_page ( p, bucket, is_bucket, is_bitmap )
54946372Sbostic char	*p;
55046372Sbostic int	bucket;
55146372Sbostic int	is_bucket;
55246372Sbostic int	is_bitmap;
55346372Sbostic {
55446372Sbostic     register int size;
55546372Sbostic     register int fd;
55646372Sbostic     register int page;
55746372Sbostic     int	wsize;
55846372Sbostic 
55946372Sbostic     size = hashp->BSIZE;
56046460Sbostic     if ( (hashp->fp == -1) && open_temp() ) return (1);
56146372Sbostic     fd = hashp->fp;
56246372Sbostic 
56346372Sbostic     if ( hashp->LORDER != BYTE_ORDER ) {
56446372Sbostic 	register int i;
56546372Sbostic 	register int max;
56646372Sbostic 
56746372Sbostic 	if ( is_bitmap ) {
56846372Sbostic 	    max = hashp->BSIZE >> 2;	/* divide by 4 */
56946372Sbostic 	    for ( i=0; i < max; i++ ) {
57046372Sbostic 		BLSWAP(((long *)p)[i]);
57146372Sbostic 	    }
57246372Sbostic 	} else {
57346372Sbostic 	    max = ((u_short *)p)[0] + 2;
57446372Sbostic 	    for ( i=0; i <= max; i++ ) {
57546372Sbostic 		BSSWAP(((u_short *)p)[i]);
57646372Sbostic 	    }
57746372Sbostic 	}
57846372Sbostic     }
57946372Sbostic     if (is_bucket ) page = BUCKET_TO_PAGE (bucket);
58046372Sbostic     else page = OADDR_TO_PAGE ( bucket );
58146500Sbostic     if ((lseek ( fd, page << hashp->BSHIFT, SEEK_SET ) == -1) ||
58246372Sbostic 	((wsize = write ( fd, p, size )) == -1 )) {
58346372Sbostic 	/* Errno is set */
58446372Sbostic 	return(-1);
58546372Sbostic     }
58646372Sbostic     if ( wsize != size ) {
58746372Sbostic 	errno = EFTYPE;
58846372Sbostic 	return(-1);
58946372Sbostic     }
59046372Sbostic     return(0);
59146372Sbostic }
59246372Sbostic #define BYTE_MASK	((1 << INT_BYTE_SHIFT) -1)
59346372Sbostic /*
59446372Sbostic     Initialize a new bitmap page.  Bitmap pages are left in memory
59546372Sbostic     once they are read in.
59646372Sbostic */
59746372Sbostic extern u_long *
59846372Sbostic __init_bitmap(pnum, nbits, ndx)
59946372Sbostic u_short	pnum;
60046372Sbostic int	nbits;
60146372Sbostic int	ndx;
60246372Sbostic {
60346372Sbostic     u_long	*ip;
60446372Sbostic     int		clearints;
60546372Sbostic     int		clearbytes;
60646372Sbostic 
60746372Sbostic     if ( !(ip = (u_long *)malloc (hashp->BSIZE)) ) return (NULL);
60846505Sbostic     hashp->exmaps++;
60946372Sbostic     clearints = ((nbits - 1) >> INT_BYTE_SHIFT) + 1;
61046372Sbostic     clearbytes = clearints << INT_TO_BYTE;
61146372Sbostic     memset ((char *)ip, 0, clearbytes );
61246372Sbostic     memset ( ((char *) ip) + clearbytes, 0xFF,
61346372Sbostic 		hashp->BSIZE-clearbytes );
61446372Sbostic     ip[clearints-1] = ALL_SET << (nbits & BYTE_MASK);
61546372Sbostic     SETBIT(ip, 0);
61646372Sbostic     hashp->BITMAPS[ndx] = pnum;
61746372Sbostic     hashp->mapp[ndx] = ip;
61846372Sbostic     return(ip);
61946372Sbostic }
62046372Sbostic static int
62146372Sbostic first_free ( map )
62246372Sbostic u_long map;
62346372Sbostic {
62446372Sbostic     register u_long      mask;
62546372Sbostic     register u_long      i;
62646372Sbostic 
62746372Sbostic     mask = 0x1;
62846372Sbostic     for ( i=0; i < BITS_PER_MAP; i++ ) {
62946372Sbostic 	if ( !(mask & map) ) return(i);
63046372Sbostic 	mask = mask << 1;
63146372Sbostic     }
63246372Sbostic     return ( i );
63346372Sbostic }
63446372Sbostic 
63546372Sbostic static u_short
63646372Sbostic overflow_page ( )
63746372Sbostic {
63846372Sbostic     register	int max_free;
63946372Sbostic     register	int splitnum;
64046372Sbostic     register	u_long *freep;
64146372Sbostic     register	int offset;
64246372Sbostic     u_short	addr;
64346372Sbostic     int		in_use_bits;
64446372Sbostic     int		free_page, free_bit;
64546372Sbostic     int		i, j, bit;
64646372Sbostic #ifdef DEBUG2
64746372Sbostic     int	tmp1, tmp2;
64846372Sbostic #endif
64946372Sbostic 
65046372Sbostic     splitnum = __log2(hashp->MAX_BUCKET);
65146372Sbostic     max_free = hashp->SPARES[splitnum];
65246372Sbostic 
65346372Sbostic     free_page = (max_free-1) >> (hashp->BSHIFT + BYTE_SHIFT);
65446372Sbostic     free_bit  = (max_free-1) & ((hashp->BSIZE << BYTE_SHIFT) - 1);
65546372Sbostic 
65646372Sbostic     /* Look through all the free maps to find the first free block */
65746372Sbostic     for ( i = 0; i <= free_page; i++ ) {
65846372Sbostic 	if (!(freep = (u_long *)hashp->mapp[i]) ) return(NULL);
65946372Sbostic 	if ( i == free_page ) in_use_bits = free_bit;
66046372Sbostic 	else in_use_bits = (hashp->BSIZE << BYTE_SHIFT) -1;
66146372Sbostic 
66246372Sbostic 	for (j = 0, bit = 0; bit <= in_use_bits; j++, bit += BITS_PER_MAP ) {
66346372Sbostic 	     if ( freep[j] != ALL_SET ) goto found;
66446372Sbostic 	}
66546372Sbostic     }
66646372Sbostic     /* No Free Page Found */
66746372Sbostic     hashp->SPARES[splitnum]++;
66846372Sbostic     offset = hashp->SPARES[splitnum] -
66946372Sbostic 	     (splitnum ? hashp->SPARES[splitnum-1] : 0);
67046372Sbostic 
67146372Sbostic     /* Check if we need to allocate a new bitmap page */
67246372Sbostic     if ( free_bit == (hashp->BSIZE << BYTE_SHIFT) - 1 ) {
67346372Sbostic 	free_page++;
67446372Sbostic 	if ( free_page >= NCACHED ) {
67546372Sbostic 	    fprintf ( stderr,
67646372Sbostic 		"HASH: Out of overflow pages.  Increase page size\n" );
67746372Sbostic 	    return(NULL);
67846372Sbostic 	}
67946372Sbostic 	/*
68046372Sbostic 	    This is tricky.  The 1 indicates that you want the
68146372Sbostic 	    new page allocated with 1 clear bit.  Actually, you
68246372Sbostic 	    are going to allocate 2 pages from this map.  The first
68346372Sbostic 	    is going to be the map page, the second is the overflow
68446372Sbostic 	    page we were looking for.  The init_bitmap routine
68546372Sbostic 	    automatically, sets the first bit of itself to indicate
68646372Sbostic 	    that the bitmap itself is in use.  We would explicitly
68746372Sbostic 	    set the second bit, but don't have to if we tell init_bitmap
68846372Sbostic 	    not to leave it clear in the first place.
68946372Sbostic 	*/
69046372Sbostic 	__init_bitmap ( OADDR_OF(splitnum, offset), 1, free_page );
69146372Sbostic 	hashp->SPARES[splitnum]++;
69246372Sbostic #ifdef DEBUG2
69346372Sbostic 	free_bit = 2;
69446372Sbostic #endif
69546372Sbostic 	offset++;
69646372Sbostic     } else {
69746372Sbostic 	/*
69846372Sbostic 		Free_bit addresses the last used bit.  Bump it to
69946372Sbostic 		address the first available bit.
70046372Sbostic 	*/
70146372Sbostic 	free_bit++;
70246372Sbostic 	SETBIT ( freep, free_bit );
70346372Sbostic     }
70446372Sbostic 
70546372Sbostic     /* Calculate address of the new overflow page */
70646372Sbostic     if ( offset > SPLITMASK ) {
70746372Sbostic 	fprintf ( stderr,
70846372Sbostic 	    "HASH: Out of overflow pages.  Increase page size\n" );
70946372Sbostic 	return(NULL);
71046372Sbostic     }
71146372Sbostic     addr = OADDR_OF(splitnum, offset);
71246372Sbostic #ifdef DEBUG2
71346372Sbostic     fprintf ( stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n",
71446372Sbostic 		addr, free_bit, free_page );
71546372Sbostic #endif
71646372Sbostic     return(addr);
71746372Sbostic 
71846372Sbostic found:
71946372Sbostic     bit = bit + first_free(freep[j]);
72046372Sbostic     SETBIT(freep,bit);
72146372Sbostic #ifdef DEBUG2
72246372Sbostic     tmp1 = bit;
72346372Sbostic     tmp2 = i;
72446372Sbostic #endif
72546372Sbostic     /*
72646372Sbostic 	Bits are addressed starting with 0, but overflow pages are
72746372Sbostic 	addressed beginning at 1. Bit is a bit addressnumber, so we
72846372Sbostic 	need to increment it to convert it to a page number.
72946372Sbostic     */
73046372Sbostic     bit = 1 + bit + (i * (hashp->BSIZE << BYTE_SHIFT));
73146372Sbostic 
73246372Sbostic     /* Calculate the split number for this page */
73346372Sbostic     for ( i = 0; (i < splitnum) && (bit > hashp->SPARES[i]); i++ );
73446372Sbostic     offset =(i ? bit - hashp->SPARES[i-1] : bit );
73546372Sbostic     if ( offset >= SPLITMASK ) return(NULL);/* Out of overflow pages */
73646372Sbostic     addr = OADDR_OF(i, offset);
73746372Sbostic #ifdef DEBUG2
73846372Sbostic     fprintf ( stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n",
73946372Sbostic 		addr, tmp1, tmp2 );
74046372Sbostic #endif
74146372Sbostic 
74246372Sbostic     /* Allocate and return the overflow page */
74346372Sbostic     return (addr);
74446372Sbostic }
74546372Sbostic 
74646372Sbostic /*
74746372Sbostic     Mark this overflow page as free.
74846372Sbostic */
74946372Sbostic __free_ovflpage ( obufp )
75046372Sbostic BUFHEAD	*obufp;
75146372Sbostic {
75246372Sbostic     register u_short addr = obufp->addr;
75346372Sbostic     int	free_page, free_bit;
75446372Sbostic     int bit_address;
75546372Sbostic     u_short ndx;
75646372Sbostic     u_long *freep;
75746372Sbostic     int j;
75846372Sbostic 
75946372Sbostic #ifdef DEBUG1
76046372Sbostic     fprintf ( stderr, "Freeing %d\n", addr );
76146372Sbostic #endif
76246372Sbostic     ndx = (((u_short)addr) >> SPLITSHIFT);
76346372Sbostic     bit_address = (ndx ? hashp->SPARES[ndx-1] : 0) + (addr & SPLITMASK) - 1;
76446372Sbostic     free_page = (bit_address >> (hashp->BSHIFT + BYTE_SHIFT));
76546372Sbostic     free_bit  = bit_address & ((hashp->BSIZE << BYTE_SHIFT) - 1);
76646372Sbostic 
76746372Sbostic     freep = hashp->mapp[free_page];
76846372Sbostic     assert(freep);
76946372Sbostic     CLRBIT(freep, free_bit);
77046372Sbostic #ifdef DEBUG2
77146372Sbostic     fprintf ( stderr, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n",
77246372Sbostic 		obufp->addr, free_bit, free_page );
77346372Sbostic #endif
77446372Sbostic     __reclaim_buf ( obufp );
77546372Sbostic     return;
77646372Sbostic }
77746372Sbostic 
77846372Sbostic /*
77946372Sbostic 0 success
78046372Sbostic -1 failure
78146372Sbostic */
78246372Sbostic static int
78346372Sbostic open_temp()
78446372Sbostic {
78546416Sbostic     sigset_t	set, oset;
78646499Sbostic     char	namestr[] = "_hashXXXXXX";
78746372Sbostic 
78846416Sbostic     /* Block signals; make sure file goes away at process exit. */
78946416Sbostic     sigemptyset(&set);
79046416Sbostic     sigaddset(&set, SIGHUP);
79146416Sbostic     sigaddset(&set, SIGINT);
79246416Sbostic     sigaddset(&set, SIGQUIT);
79346416Sbostic     sigaddset(&set, SIGTERM);
79446416Sbostic     (void)sigprocmask(SIG_BLOCK, &set, &oset);
79546416Sbostic     if ((hashp->fp = mkstemp ( namestr )) != -1) {
79646416Sbostic 	(void)unlink(namestr);
79746416Sbostic 	(void)fcntl(hashp->fp, F_SETFD, 1);
79846372Sbostic     }
79946416Sbostic     (void)sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
80046416Sbostic     return(hashp->fp != -1 ? 0 : -1);
80146372Sbostic }
80246372Sbostic 
80346372Sbostic /*
80446372Sbostic     We have to know that the key will fit, but the
80546372Sbostic     last entry on the page is an overflow pair, so we
80646372Sbostic     need to shift things.
80746372Sbostic */
80846372Sbostic static void
80946372Sbostic squeeze_key ( sp, key, val )
81046372Sbostic u_short	*sp;
81146372Sbostic DBT	*key;
81246372Sbostic DBT	*val;
81346372Sbostic {
81446372Sbostic     register	char	*p = (char *)sp;
81546372Sbostic     u_short	free_space, off;
81646372Sbostic     u_short	pageno, n;
81746372Sbostic 
81846372Sbostic     n = sp[0];
81946372Sbostic     free_space = FREESPACE(sp);
82046372Sbostic     off = OFFSET(sp);
82146372Sbostic 
82246372Sbostic     pageno = sp[n-1];
82346372Sbostic     off -= key->size;
82446372Sbostic     sp[n-1] = off;
82546372Sbostic     bcopy ( key->data, p + off, key->size );
82646372Sbostic     off -= val->size;
82746372Sbostic     sp[n] = off;
82846372Sbostic     bcopy ( val->data, p + off, val->size );
82946372Sbostic     sp[0] = n+2;
83046372Sbostic     sp[n+1] = pageno;
83146372Sbostic     sp[n+2] = OVFLPAGE;
83246372Sbostic     FREESPACE(sp) = free_space - PAIRSIZE(key,val);
83346372Sbostic     OFFSET(sp) = off;
83446372Sbostic }
83546372Sbostic 
83646372Sbostic #ifdef DEBUG4
83746372Sbostic print_chain ( addr )
83846372Sbostic short	addr;
83946372Sbostic {
84046372Sbostic 	BUFHEAD	*bufp;
84146372Sbostic 	short	*bp;
84246372Sbostic 	short	oaddr;
84346372Sbostic 
84446372Sbostic 	fprintf ( stderr, "%d ", addr );
84546372Sbostic 	bufp = __get_buf ( (int)addr, NULL, 0 );
84646372Sbostic 	bp = (short *)bufp->page;
84746372Sbostic 	while ( bp[0] &&
84846372Sbostic 		((bp[bp[0]] == OVFLPAGE) ||
84946372Sbostic 		 ((bp[0] > 2) && bp[2] < REAL_KEY))) {
85046372Sbostic 		oaddr = bp[bp[0]-1];
85146372Sbostic 		fprintf ( stderr, "%d ", (int)oaddr );
85246372Sbostic 		bufp = __get_buf ( (int)oaddr, bufp, 0 );
85346372Sbostic 		bp = (short *)bufp->page;
85446372Sbostic 	}
85546372Sbostic 	fprintf ( stderr, "\n" );
85646372Sbostic }
85746372Sbostic #endif
858