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