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