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