xref: /csrg-svn/lib/libc/db/hash/hash.c (revision 46416)
146366Sbostic /*-
246366Sbostic  * Copyright (c) 1990 The Regents of the University of California.
346366Sbostic  * All rights reserved.
446366Sbostic  *
546366Sbostic  * This code is derived from software contributed to Berkeley by
646366Sbostic  * Margo Seltzer.
746366Sbostic  *
846366Sbostic  * %sccs.include.redist.c%
946366Sbostic  */
1046366Sbostic 
1146366Sbostic #if defined(LIBC_SCCS) && !defined(lint)
12*46416Sbostic static char sccsid[] = "@(#)hash.c	5.2 (Berkeley) 02/14/91";
1346366Sbostic #endif /* LIBC_SCCS and not lint */
1446366Sbostic 
1546366Sbostic #include <sys/param.h>
1646366Sbostic #include <sys/file.h>
1746366Sbostic #include <sys/stat.h>
1846366Sbostic #include <errno.h>
1946366Sbostic #include <assert.h>
2046366Sbostic #include <db.h>
2146366Sbostic #include "hash.h"
2246366Sbostic #include <string.h>
2346366Sbostic 
2446366Sbostic /* For systems that do not have O_ACCMODE */
2546366Sbostic #ifndef O_ACCMODE
2646366Sbostic #define O_ACCMODE       (O_RDONLY|O_WRONLY|O_RDWR)
2746366Sbostic #endif
2846366Sbostic 
2946366Sbostic /* Fast arithmetic, relying on powers of 2, */
3046366Sbostic 
3146366Sbostic #define MOD(x,y)		((x) & ((y)-1))
3246366Sbostic #define RETURN_ERROR(ERR,LOC)	{ save_errno = ERR; goto LOC; }
3346366Sbostic 
3446366Sbostic /* Return values */
3546366Sbostic 
3646366Sbostic #define	SUCCESS	0
3746366Sbostic #define	ERROR	-1
3846366Sbostic #define	ABNORMAL 1
3946366Sbostic 
4046366Sbostic /* external routines */
4146366Sbostic extern char *calloc();
4246366Sbostic 
4346366Sbostic /* page.c */
4446366Sbostic extern int __get_page();
4546366Sbostic extern int __split_page();
4646366Sbostic extern int __addel();
4746366Sbostic extern int __delpair();
4846366Sbostic extern u_long *__init_bitmap();
4946366Sbostic 
5046366Sbostic /* big.c */
5146366Sbostic extern void __big_return();
5246366Sbostic extern int __big_keydata();
5346366Sbostic extern u_short __find_last_page();
5446366Sbostic 
5546366Sbostic /* buf.c */
5646366Sbostic extern BUFHEAD	*__get_buf();
5746366Sbostic extern void __buf_init();
5846366Sbostic extern int __buf_free();
5946366Sbostic 
6046366Sbostic /* hash function */
6146366Sbostic extern int (*default_hash)();
6246366Sbostic 
6346366Sbostic /* My externals */
6446366Sbostic extern int __expand_table();
6546366Sbostic extern int __call_hash();
6646366Sbostic 
6746366Sbostic /* Internal routines */
6846366Sbostic static HTAB *init_hash();
6946366Sbostic static int hash_access();
7046366Sbostic static int flush_meta();
7146366Sbostic static int alloc_segs();
7246366Sbostic static int init_htab();
7346366Sbostic static char *hash_realloc();
7446366Sbostic static int hdestroy();
7546366Sbostic static int hash_put();
7646366Sbostic static int hash_delete();
7746366Sbostic static int hash_get();
7846366Sbostic static int hash_sync();
7946366Sbostic static int hash_close();
8046366Sbostic static int hash_seq();
8146366Sbostic static void swap_header_copy();
8246366Sbostic static void swap_header();
8346366Sbostic 
8446366Sbostic /* Local data */
8546366Sbostic 
8646366Sbostic HTAB *hashp = NULL;
8746366Sbostic 
8846366Sbostic #ifdef HASH_STATISTICS
8946366Sbostic long hash_accesses, hash_collisions, hash_expansions, hash_overflows;
9046366Sbostic #endif
9146366Sbostic 
9246366Sbostic /************************** INTERFACE ROUTINES **********************/
9346366Sbostic /* OPEN/CLOSE */
9446366Sbostic 
9546366Sbostic extern	DB *
9646366Sbostic hash_open ( file, flags, mode, info )
9746366Sbostic char	*file;
9846366Sbostic int	flags;
9946366Sbostic int	mode;
10046366Sbostic HASHINFO	*info;		/* Special directives for create */
10146366Sbostic {
10246366Sbostic     int		buckets;
10346366Sbostic     int		bpages;
10446366Sbostic     int		hdrsize;
10546366Sbostic     int		i;
10646366Sbostic     int		new_table = 0;
10746366Sbostic     int		nkey;
10846366Sbostic     int		nsegs;
10946366Sbostic     int		save_errno;
11046366Sbostic     struct	stat statbuf;
11146366Sbostic     DB		*dbp;
11246366Sbostic 
11346366Sbostic 
11446366Sbostic     if ( !(hashp = (HTAB *) calloc( 1, sizeof(HTAB) ))) {
11546366Sbostic 	/* calloc should set errno */
11646366Sbostic 	return(0);
11746366Sbostic     }
11846366Sbostic     /*
11946366Sbostic 	Select flags relevant to us.
12046366Sbostic 	Even if user wants write only, we need to be able to read
12146366Sbostic 	the actual file, so we need to open it read/write.  But, the
12246366Sbostic 	field in the hashp structure needs to be accurate so that
12346366Sbostic 	we can check accesses.
12446366Sbostic     */
12546366Sbostic     flags = flags & (O_CREAT | O_EXCL | O_RDONLY | O_RDWR | O_TRUNC | O_WRONLY);
12646366Sbostic     hashp->flags = flags;
12746366Sbostic     if ( flags & O_WRONLY )  flags = (flags & ~O_WRONLY) | O_RDWR;
12846366Sbostic 
12946366Sbostic     if ( !file ||
13046366Sbostic 	 (flags & O_TRUNC) ||
13146366Sbostic 	 (stat ( file, &statbuf ) && (errno == ENOENT)) ) {
13246366Sbostic 	 new_table = 1;
13346366Sbostic     }
13446366Sbostic 
13546366Sbostic     if ( file && ((hashp->fp = open ( file, flags, mode )) == -1)) {
13646366Sbostic 	RETURN_ERROR (errno, error0);
13746366Sbostic     }
138*46416Sbostic     (void)fcntl(hashp->fp, F_SETFD, 1);
13946366Sbostic 
14046366Sbostic     if ( new_table ) {
14146366Sbostic 	if ( !(hashp = init_hash( info )) ) {
14246366Sbostic 	    RETURN_ERROR(errno,error1);
14346366Sbostic 	}
14446366Sbostic     } else {
14546366Sbostic 	/* Table already exists */
14646366Sbostic 	if ( info && info->hash ) hashp->hash = info->hash;
14746366Sbostic 	else hashp->hash = default_hash;
14846366Sbostic 
14946366Sbostic 	hdrsize = read ( hashp->fp, &hashp->hdr, sizeof(HASHHDR) );
15046366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
15146366Sbostic 	swap_header ( );
15246366Sbostic #endif
15346366Sbostic 	if ( hdrsize == -1 ) {
15446366Sbostic 	    RETURN_ERROR(errno, error1);
15546366Sbostic 	}
15646366Sbostic 	if ( hdrsize != sizeof(HASHHDR) ) {
15746366Sbostic 	    RETURN_ERROR(EFTYPE, error1);
15846366Sbostic 	}
15946366Sbostic 
16046366Sbostic 	/* Verify file type, versions and hash function */
16146366Sbostic 	if ( hashp->MAGIC != HASHMAGIC )
16246366Sbostic 	    RETURN_ERROR ( EFTYPE, error1 );
16346366Sbostic 	if ( hashp->VERSION != VERSION_NO )
16446366Sbostic 	    RETURN_ERROR ( EFTYPE, error1 );
16546366Sbostic 	if (hashp->hash ( CHARKEY, sizeof(CHARKEY) ) != hashp->H_CHARKEY ) {
16646366Sbostic 	    RETURN_ERROR ( EFTYPE, error1 );
16746366Sbostic 	}
16846366Sbostic 
16946366Sbostic 	/*
17046366Sbostic 	    Figure out how many segments we need.
17146366Sbostic 	*/
17246366Sbostic 	nsegs = (hashp->MAX_BUCKET + hashp->SGSIZE -1)/ hashp->SGSIZE;
17346366Sbostic 	hashp->nsegs = 0;
17446366Sbostic 	if (alloc_segs( nsegs )) {
17546366Sbostic 	    /*
17646366Sbostic 		If alloc_segs fails, table will have been destroyed
17746366Sbostic 		and errno will have been set.
17846366Sbostic 	    */
17946366Sbostic 	    return( (DB *) NULL );
18046366Sbostic 	}
18146366Sbostic 
18246366Sbostic 	/* Read in bitmaps */
18346366Sbostic 	bpages = (hashp->SPARES[__log2(hashp->MAX_BUCKET)] +
18446366Sbostic 		  (hashp->BSIZE << BYTE_SHIFT) - 1) >>
18546366Sbostic 		  (hashp->BSHIFT + BYTE_SHIFT);
18646366Sbostic 
18746366Sbostic 	hashp->mapp[0] = (u_long *)malloc(bpages<<hashp->BSHIFT);
18846366Sbostic 	if ( !hashp->mapp[0] ) {
18946366Sbostic 	    RETURN_ERROR(errno, error2);
19046366Sbostic 	}
19146366Sbostic 	for ( i = 0; i < bpages; i++ ) {
19246366Sbostic 	    hashp->mapp[i] = &hashp->mapp[0][i<<hashp->BSHIFT];
19346366Sbostic 	    if (__get_page ((char *)hashp->mapp[i],
19446366Sbostic 		hashp->BITMAPS[i], 0, 1, 1)) {
19546366Sbostic 		    RETURN_ERROR(errno, error2);
19646366Sbostic 	    }
19746366Sbostic 	}
19846366Sbostic 
19946366Sbostic     }
20046366Sbostic 
20146366Sbostic     /* Initialize Buffer Manager */
20246366Sbostic     if ( info && info->ncached ) {
20346366Sbostic 	__buf_init (info->ncached);
20446366Sbostic     } else {
20546366Sbostic 	__buf_init (DEF_BUFSIZE);
20646366Sbostic     }
20746366Sbostic 
20846366Sbostic     hashp->new_file = new_table;
20946366Sbostic     hashp->save_file = (file != NULL);
21046366Sbostic     hashp->cbucket = -1;
21146366Sbostic     if ( !(dbp = (DB *)malloc ( sizeof (DB) )) ) {
21246366Sbostic 	save_errno = errno;
21346366Sbostic 	hdestroy();
21446366Sbostic 	errno = save_errno;
21546366Sbostic 	return ( NULL );
21646366Sbostic     }
21746366Sbostic     dbp->internal = (char *)hashp;
21846366Sbostic     dbp->close = hash_close;
21946366Sbostic     dbp->delete = hash_delete;
22046366Sbostic     dbp->get = hash_get;
22146366Sbostic     dbp->put = hash_put;
22246366Sbostic     dbp->seq = hash_seq;
22346366Sbostic     dbp->sync = hash_sync;
22446366Sbostic 
22546366Sbostic #ifdef DEBUG
22646366Sbostic 	(void)fprintf(stderr, "%s\n%s%x\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%x\n%s%x\n%s%d\n%s%d\n",
22746366Sbostic 		"init_htab:",
22846366Sbostic 		"TABLE POINTER   ", hashp,
22946366Sbostic 		"BUCKET SIZE     ", hashp->BSIZE,
23046366Sbostic 		"BUCKET SHIFT    ", hashp->BSHIFT,
23146366Sbostic 		"DIRECTORY SIZE  ", hashp->DSIZE,
23246366Sbostic 		"SEGMENT SIZE    ", hashp->SGSIZE,
23346366Sbostic 		"SEGMENT SHIFT   ", hashp->SSHIFT,
23446366Sbostic 		"FILL FACTOR     ", hashp->FFACTOR,
23546366Sbostic 		"MAX BUCKET      ", hashp->MAX_BUCKET,
23646366Sbostic 		"HIGH MASK       ", hashp->HIGH_MASK,
23746366Sbostic 		"LOW  MASK       ", hashp->LOW_MASK,
23846366Sbostic 		"NSEGS           ", hashp->nsegs,
23946366Sbostic 		"NKEYS           ", hashp->NKEYS );
24046366Sbostic #endif
24146366Sbostic #ifdef HASH_STATISTICS
24246366Sbostic 	hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0;
24346366Sbostic #endif
24446366Sbostic     return (dbp);
24546366Sbostic 
24646366Sbostic error2:
24746366Sbostic     (void)hdestroy();
24846366Sbostic     errno = save_errno;
24946366Sbostic     hashp->errno = errno;
25046366Sbostic     return ( (DB *)NULL );
25146366Sbostic 
25246366Sbostic error1:
25346366Sbostic     (void) close ( hashp->fp );
25446366Sbostic 
25546366Sbostic error0:
25646366Sbostic     free ( hashp );
25746366Sbostic     errno = save_errno;
25846366Sbostic     hashp->errno = errno;
25946366Sbostic     return ( (DB *) NULL );
26046366Sbostic }
26146366Sbostic 
26246366Sbostic static int
26346366Sbostic hash_close (dbp)
26446366Sbostic DB	*dbp;
26546366Sbostic {
26646366Sbostic 	if ( !dbp ) {
26746366Sbostic 	    return(ERROR);
26846366Sbostic 	}
26946366Sbostic 	hashp = (HTAB *)dbp->internal;
27046366Sbostic 	return (hdestroy());
27146366Sbostic }
27246366Sbostic 
27346366Sbostic 
27446366Sbostic /************************** LOCAL CREATION ROUTINES **********************/
27546366Sbostic static HTAB *
27646366Sbostic init_hash( info )
27746366Sbostic HASHINFO *info;
27846366Sbostic {
27946366Sbostic 	int	nelem;
28046366Sbostic 
28146366Sbostic 	nelem = 1;
28246366Sbostic 
28346366Sbostic 	hashp->LORDER	= BYTE_ORDER;
28446366Sbostic 	hashp->BSIZE    = DEF_BUCKET_SIZE;
28546366Sbostic 	hashp->BSHIFT   = DEF_BUCKET_SHIFT;
28646366Sbostic 	hashp->SGSIZE   = DEF_SEGSIZE;
28746366Sbostic 	hashp->SSHIFT   = DEF_SEGSIZE_SHIFT;
28846366Sbostic 	hashp->DSIZE    = DEF_DIRSIZE;
28946366Sbostic 	hashp->FFACTOR  = DEF_FFACTOR;
29046366Sbostic 	hashp->hash     = default_hash;
29146366Sbostic 	bzero (hashp->SPARES, sizeof (hashp->SPARES));
29246366Sbostic 
29346366Sbostic 	if ( info ) {
29446366Sbostic 	    if ( info->bsize )   {
29546366Sbostic 		/* Round pagesize up to power of 2 */
29646366Sbostic 		hashp->BSHIFT  = __log2(info->bsize);
29746366Sbostic 		hashp->BSIZE   = 1 << hashp->BSHIFT;
29846366Sbostic 		if ( hashp->BSIZE > MAX_BSIZE ) {
29946366Sbostic 		    errno = EINVAL;
30046366Sbostic 		    return(0);
30146366Sbostic 		}
30246366Sbostic 	    }
30346366Sbostic 	    if ( info->ffactor )  hashp->FFACTOR = info->ffactor;
30446366Sbostic 	    if ( info->hash ) hashp->hash    = info->hash;
30546366Sbostic 	    if ( info->nelem ) nelem = info->nelem;
30646366Sbostic 	    if ( info->lorder ) {
30746366Sbostic 		if ( info->lorder != BIG_ENDIAN &&
30846366Sbostic 		     info->lorder != LITTLE_ENDIAN) {
30946366Sbostic 		    errno = EINVAL;
31046366Sbostic 		    return(0);
31146366Sbostic 		}
31246366Sbostic 		hashp->LORDER = info->lorder;
31346366Sbostic 	    }
31446366Sbostic 	}
31546366Sbostic 
31646366Sbostic 	/* init_htab should destroy the table and set errno if it fails */
31746366Sbostic 	if ( init_htab ( nelem ) ) {
31846366Sbostic 	    return(0);
31946366Sbostic 	} else {
32046366Sbostic 	    return(hashp);
32146366Sbostic 	}
32246366Sbostic }
32346366Sbostic 
32446366Sbostic /*
32546366Sbostic     This calls alloc_segs which may run out of memory.
32646366Sbostic     Alloc_segs will destroy the table and set errno,
32746366Sbostic     so we just pass the error information along.
32846366Sbostic 
32946366Sbostic     Returns 0 on No Error
33046366Sbostic 
33146366Sbostic */
33246366Sbostic static int
33346366Sbostic init_htab ( nelem )
33446366Sbostic int	nelem;
33546366Sbostic {
33646366Sbostic 	register SEGMENT	*segp;
33746366Sbostic 	register int nbuckets;
33846366Sbostic 	register int nsegs;
33946366Sbostic 	int	l2;
34046366Sbostic 
34146366Sbostic  /*
34246366Sbostic   * Divide number of elements by the fill factor and determine a desired
34346366Sbostic   * number of buckets.  Allocate space for the next greater power of
34446366Sbostic   * two number of buckets
34546366Sbostic   */
34646366Sbostic 	nelem = (nelem - 1) / hashp->FFACTOR + 1;
34746366Sbostic 
34846366Sbostic 	l2 = __log2(nelem);
34946366Sbostic 	nbuckets = 1 << l2;
35046366Sbostic 	nbuckets = MAX ( nbuckets, 2 );
35146366Sbostic 
35246366Sbostic 	hashp->SPARES[l2] = l2 + 1;
35346366Sbostic 	hashp->SPARES[l2+1] = l2 + 1;
35446366Sbostic 	/*
35546366Sbostic 	    First bitmap page is at:
35646366Sbostic 		splitpoint l2
35746366Sbostic 		page offset 1
35846366Sbostic 	*/
35946366Sbostic 	__init_bitmap ( OADDR_OF(l2,1), l2+1, 0 );
36046366Sbostic 
36146366Sbostic 	hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1;
36246366Sbostic 	hashp->HIGH_MASK = (nbuckets << 1) - 1;
36346366Sbostic 	hashp->HDRPAGES = ((MAX(sizeof(HASHHDR),MINHDRSIZE) - 1) >>
36446366Sbostic 			  hashp->BSHIFT) + 1;
36546366Sbostic 
36646366Sbostic 	nsegs = (nbuckets - 1) / hashp->SGSIZE + 1;
36746366Sbostic 	nsegs = 1 << __log2(nsegs);
36846366Sbostic 
36946366Sbostic 	if ( nsegs > hashp->DSIZE ) {
37046366Sbostic 	    hashp->DSIZE  = nsegs;
37146366Sbostic 	}
37246366Sbostic 
37346366Sbostic 	return (alloc_segs ( nsegs ) );
37446366Sbostic }
37546366Sbostic 
37646366Sbostic /********************** DESTROY/CLOSE ROUTINES ************************/
37746366Sbostic 
37846366Sbostic /*
37946366Sbostic     Flushes any changes to the file if necessary and
38046366Sbostic     destroys the hashp structure, freeing all allocated
38146366Sbostic     space.
38246366Sbostic */
38346366Sbostic static int
38446366Sbostic hdestroy()
38546366Sbostic {
38646366Sbostic 	int	save_errno;
38746366Sbostic 	int	i;
38846366Sbostic 
38946366Sbostic 	save_errno = 0;
39046366Sbostic 
39146366Sbostic 	if (hashp != NULL) {
39246366Sbostic #ifdef HASH_STATISTICS
39346366Sbostic 	 (void)	fprintf(stderr,	"hdestroy: accesses %ld collisions %ld\n",
39446366Sbostic 			hash_accesses, hash_collisions);
39546366Sbostic 	 (void)	fprintf(stderr, "hdestroy: expansions %ld\n",
39646366Sbostic 			hash_expansions);
39746366Sbostic 	 (void)	fprintf(stderr, "hdestroy: overflows %ld\n",
39846366Sbostic 			hash_overflows);
39946366Sbostic 	 (void)	fprintf(stderr,	"keys %ld maxp %d segmentcount %d\n",
40046366Sbostic 			hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs);
40146366Sbostic 
40246366Sbostic 	for ( i = 0; i < NCACHED; i++ ) {
40346366Sbostic 	    (void) fprintf ( stderr, "spares[%d] = %d\n", i, hashp->SPARES[i] );
40446366Sbostic 	}
40546366Sbostic #endif
40646366Sbostic 
40746366Sbostic 		/*
40846366Sbostic 		    Call on buffer manager to free buffers, and if
40946366Sbostic 		    required, write them to disk
41046366Sbostic 		*/
41146366Sbostic 		if (__buf_free(1, hashp->save_file)) {
41246366Sbostic 		    save_errno = errno;
41346366Sbostic 		}
41446366Sbostic 		if ( hashp->dir ) {
41546366Sbostic 		    (void)free(*hashp->dir);	/* Free initial segments */
41646366Sbostic 		    /* Free extra segments */
41746366Sbostic 		    while ( hashp->exsegs-- ) {
41846366Sbostic 			(void)free ( hashp->dir[--hashp->nsegs] );
41946366Sbostic 		    }
42046366Sbostic 		    (void)free(hashp->dir);
42146366Sbostic 		}
42246366Sbostic 		if (flush_meta() && !save_errno) {
42346366Sbostic 		    save_errno = errno;
42446366Sbostic 		}
42546366Sbostic 		if ( hashp->save_file ) (void)close (hashp->fp);
42646366Sbostic 		(void)free(hashp);
42746366Sbostic 		hashp = NULL;
42846366Sbostic 	}
42946366Sbostic 	if ( save_errno ) {
43046366Sbostic 	    errno = save_errno;
43146366Sbostic 	    return(ERROR);
43246366Sbostic 	} else {
43346366Sbostic 	    return(SUCCESS);
43446366Sbostic 	}
43546366Sbostic }
43646366Sbostic 
43746366Sbostic /*
43846366Sbostic     Write modified pages to disk
43946366Sbostic     0 == OK
44046366Sbostic     -1 ERROR
44146366Sbostic */
44246366Sbostic static int
44346366Sbostic hash_sync(dbp)
44446366Sbostic DB	*dbp;
44546366Sbostic {
44646366Sbostic 	if ( !dbp ) {
44746366Sbostic 	    return (ERROR);
44846366Sbostic 	}
44946366Sbostic 
45046366Sbostic 	hashp = (HTAB *)dbp->internal;
45146366Sbostic 
45246366Sbostic 	if (!hashp->save_file) return(0);
45346366Sbostic 	if ( __buf_free ( 0, 1 )  || flush_meta()) {
45446366Sbostic 	    return(ERROR);
45546366Sbostic 	}
45646366Sbostic 	hashp->new_file = 0;
45746366Sbostic 	return(0);
45846366Sbostic }
45946366Sbostic 
46046366Sbostic /*
46146366Sbostic 0 == OK
46246366Sbostic -1 indicates that errno should be set
46346366Sbostic */
46446366Sbostic static int
46546366Sbostic flush_meta()
46646366Sbostic {
46746366Sbostic     	int	fp;
46846366Sbostic 	int	hdrsize;
46946366Sbostic 	int	i;
47046366Sbostic 	int	wsize;
47146366Sbostic 	HASHHDR	*whdrp;
47246366Sbostic 	HASHHDR	whdr;
47346366Sbostic 
47446366Sbostic 	if (!hashp->save_file) return(0);
47546366Sbostic 	hashp->MAGIC = HASHMAGIC;
47646366Sbostic 	hashp->VERSION = VERSION_NO;
47746366Sbostic 	hashp->H_CHARKEY = hashp->hash ( CHARKEY, sizeof(CHARKEY));
47846366Sbostic 
47946366Sbostic 	fp = hashp->fp;
48046366Sbostic 	whdrp = &hashp->hdr;
48146366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
48246366Sbostic 	whdrp = &whdr;
48346366Sbostic 	swap_header_copy( &hashp->hdr, whdrp );
48446366Sbostic #endif
48546366Sbostic 	if ( (lseek (fp, 0, L_SET) == -1 ) ||
48646366Sbostic 	     ((wsize = write ( fp, whdrp, sizeof(HASHHDR))) == -1)) {
48746366Sbostic 	    return(-1);
48846366Sbostic 	} else if ( wsize != sizeof(HASHHDR) ) {
48946366Sbostic 	    errno = EFTYPE;
49046366Sbostic 	    hashp->errno = errno;
49146366Sbostic 	    return(-1);
49246366Sbostic 	}
49346366Sbostic 	for ( i = 0; i < NCACHED; i++ ) {
49446366Sbostic 	    if ( hashp->mapp[i] ) {
49546366Sbostic 		if (!__put_page((char *)hashp->mapp[i],
49646366Sbostic 		    hashp->BITMAPS[i], 0, 1)){
49746366Sbostic 		    return(-1);
49846366Sbostic 		}
49946366Sbostic 	    }
50046366Sbostic 	}
50146366Sbostic 	return(0);
50246366Sbostic }
50346366Sbostic /*******************************SEARCH ROUTINES *****************************/
50446366Sbostic /*
50546366Sbostic     All the access routines return
50646366Sbostic 	0 on SUCCESS
50746366Sbostic 	1 to indicate an external ERROR (i.e. key not found, etc)
50846366Sbostic 	-1 to indicate an internal ERROR (i.e. out of memory, etc)
50946366Sbostic */
51046366Sbostic static int
51146366Sbostic hash_get ( dbp, key, data )
51246366Sbostic DB	*dbp;
51346366Sbostic DBT *key, *data;
51446366Sbostic {
51546366Sbostic     if ( !dbp ) {
51646366Sbostic 	return (ERROR);
51746366Sbostic     }
51846366Sbostic     hashp = (HTAB *)dbp->internal;
51946366Sbostic     if ( hashp->flags & O_WRONLY ) {
52046366Sbostic 	errno = EBADF;
52146366Sbostic 	hashp->errno = errno;
52246366Sbostic 	return ( ERROR );
52346366Sbostic     }
52446366Sbostic     return ( hash_access ( HASH_GET, key, data ) );
52546366Sbostic }
52646366Sbostic 
52746366Sbostic static int
52846366Sbostic hash_put ( dbp, key, data, flag )
52946366Sbostic DB	*dbp;
53046366Sbostic DBT 	*key, *data;
53146366Sbostic int	flag;
53246366Sbostic {
53346366Sbostic     if ( !dbp ) {
53446366Sbostic 	return (ERROR);
53546366Sbostic     }
53646366Sbostic     hashp = (HTAB *)dbp->internal;
53746366Sbostic     if ( (hashp->flags & O_ACCMODE) == O_RDONLY ) {
53846366Sbostic 	errno = EBADF;
53946366Sbostic 	hashp->errno = errno;
54046366Sbostic 	return ( ERROR );
54146366Sbostic     }
54246366Sbostic     if ( flag == R_NOOVERWRITE ) {
54346366Sbostic 	return ( hash_access ( HASH_PUTNEW, key, data ) );
54446366Sbostic     } else {
54546366Sbostic 	return ( hash_access ( HASH_PUT, key, data ) );
54646366Sbostic     }
54746366Sbostic }
54846366Sbostic 
54946366Sbostic static int
55046366Sbostic hash_delete ( dbp, key, flags )
55146366Sbostic DB	*dbp;
55246366Sbostic DBT 	*key;
55346366Sbostic int	flags;		/* Ignored */
55446366Sbostic {
55546366Sbostic     if ( !dbp ) {
55646366Sbostic 	return (ERROR);
55746366Sbostic     }
55846366Sbostic     hashp = (HTAB *)dbp->internal;
55946366Sbostic     if ( (hashp->flags & O_ACCMODE) == O_RDONLY ) {
56046366Sbostic 	errno = EBADF;
56146366Sbostic 	hashp->errno = errno;
56246366Sbostic 	return ( ERROR );
56346366Sbostic     }
56446366Sbostic     return ( hash_access ( HASH_DELETE, key, NULL ) );
56546366Sbostic }
56646366Sbostic 
56746366Sbostic /*
56846366Sbostic     Assume that hashp has been set in wrapper routine
56946366Sbostic */
57046366Sbostic static int
57146366Sbostic hash_access(action, key, val)
57246366Sbostic ACTION action;
57346366Sbostic DBT *key, *val;
57446366Sbostic {
57546366Sbostic 	register	BUFHEAD	*rbufp;
57646366Sbostic 	register	u_short	*bp;
57746366Sbostic 	register	int	ndx;
57846366Sbostic 	register 	int n;
57946366Sbostic 	register 	int off = hashp->BSIZE;
58046366Sbostic 	register	int		size;
58146366Sbostic 	register	char	*kp;
58246366Sbostic 	BUFHEAD	*bufp;
58346366Sbostic 	u_short	pageno;
58446366Sbostic 
58546366Sbostic #ifdef HASH_STATISTICS
58646366Sbostic 	hash_accesses++;
58746366Sbostic #endif
58846366Sbostic 
58946366Sbostic 	size = key->size;
59046366Sbostic 	kp = key->data;
59146366Sbostic 	rbufp = __get_buf ( __call_hash(key->data, size), NULL, 0 );
59246366Sbostic 	if ( !rbufp ) return(ERROR);
59346366Sbostic 
59446366Sbostic 	for ( bp = (u_short *)rbufp->page, n = *bp++, ndx = 1; ndx < n;  ) {
59546366Sbostic 	    if ( bp[1] >= REAL_KEY ) {
59646366Sbostic 		/* Real key/data pair */
59746366Sbostic 		if (size == off - *bp &&
59846366Sbostic 		    bcmp(kp, rbufp->page + *bp, size) == 0) {
59946366Sbostic 		    goto found;
60046366Sbostic 		}
60146366Sbostic 		off = bp[1];
60246366Sbostic #ifdef HASH_STATISTICS
60346366Sbostic 		hash_collisions++;
60446366Sbostic #endif
60546366Sbostic 	        bp += 2;
60646366Sbostic 	        ndx += 2;
60746366Sbostic 	    } else if ( bp[1] == OVFLPAGE ) {
60846366Sbostic 		rbufp = __get_buf ( *bp, rbufp, 0 );
60946366Sbostic 		if ( !rbufp ) return (ERROR);
61046366Sbostic 		/* FOR LOOP INIT */
61146366Sbostic 		bp = (u_short *)rbufp->page;
61246366Sbostic 		n = *bp++;
61346366Sbostic 		ndx = 1;
61446366Sbostic 		off = hashp->BSIZE;
61546366Sbostic 	    } else if ( bp[1] < REAL_KEY ) {
61646366Sbostic 		if ( (ndx = __find_bigpair(rbufp, ndx, kp, size )) > 0 ) {
61746366Sbostic 		    goto found;
61846366Sbostic 		} else if ( ndx == -2 ) {
61946366Sbostic 		    bufp = rbufp;
62046366Sbostic 		    if ( !(pageno = __find_last_page ( &bufp )) ) {
62146366Sbostic 			ndx = 0;
62246366Sbostic 			rbufp = bufp;
62346366Sbostic 			break;	/* FOR */
62446366Sbostic 		    }
62546366Sbostic 		    rbufp = __get_buf ( pageno, bufp, 0 );
62646366Sbostic 		    if ( !rbufp ) return (ERROR);
62746366Sbostic 		    /* FOR LOOP INIT */
62846366Sbostic 		    bp = (u_short *)rbufp->page;
62946366Sbostic 		    n = *bp++;
63046366Sbostic 		    ndx = 1;
63146366Sbostic 		    off = hashp->BSIZE;
63246366Sbostic 		} else  {
63346366Sbostic 		    return(ERROR);
63446366Sbostic 		}
63546366Sbostic 	    }
63646366Sbostic 	}
63746366Sbostic 
63846366Sbostic 	/* Not found */
63946366Sbostic 	switch ( action ) {
64046366Sbostic 	    case HASH_PUT:
64146366Sbostic 	    case HASH_PUTNEW:
64246366Sbostic 		if (__addel(rbufp, key, val)) {
64346366Sbostic 		    return(ERROR);
64446366Sbostic 		} else {
64546366Sbostic 		    return(SUCCESS);
64646366Sbostic 		}
64746366Sbostic 	    case HASH_GET:
64846366Sbostic 		return ( ABNORMAL );
64946366Sbostic 	    case HASH_DELETE:
65046366Sbostic 		return ( ABNORMAL );
65146366Sbostic 	    default:
65246366Sbostic 		return(ABNORMAL);
65346366Sbostic 	}
65446366Sbostic 
65546366Sbostic found:
65646366Sbostic 	switch (action) {
65746366Sbostic 	    case HASH_PUTNEW:
65846366Sbostic 		return(ABNORMAL);
65946366Sbostic 	    case HASH_GET:
66046366Sbostic 		bp = (u_short *)rbufp->page;
66146366Sbostic 		if (bp[ndx+1] < REAL_KEY) __big_return(rbufp, ndx, val, 0);
66246366Sbostic 		else {
66346366Sbostic 		    val->data = rbufp->page + (int) bp[ndx + 1];
66446366Sbostic 		    val->size = bp[ndx] - bp[ndx + 1];
66546366Sbostic 		}
66646366Sbostic 		break;
66746366Sbostic 	    case HASH_PUT:
66846366Sbostic 		if (__delpair (rbufp, ndx))return(ERROR);
66946366Sbostic 		if (__addel (rbufp, key, val)) return(ERROR);
67046366Sbostic 		break;
67146366Sbostic 	    case HASH_DELETE:
67246366Sbostic 		if (__delpair (rbufp, ndx))return(ERROR);
67346366Sbostic 		break;
67446366Sbostic 	}
67546366Sbostic 	return (SUCCESS);
67646366Sbostic }
67746366Sbostic 
67846366Sbostic static int
67946366Sbostic hash_seq(dbp, key, data, flag)
68046366Sbostic DB	*dbp;
68146366Sbostic DBT 	*key, *data;
68246366Sbostic int flag;
68346366Sbostic {
68446366Sbostic 	register	int bucket;
68546366Sbostic 	register	BUFHEAD	*bufp;
68646366Sbostic 	u_short	*bp;
68746366Sbostic 	u_short	ndx;
68846366Sbostic 	u_short	pageno;
68946366Sbostic 
69046366Sbostic 	if ( !dbp ) {
69146366Sbostic 	    return (ERROR);
69246366Sbostic 	}
69346366Sbostic 
69446366Sbostic 	hashp = (HTAB *)dbp->internal;
69546366Sbostic 	if ( hashp->flags & O_WRONLY ) {
69646366Sbostic 	    errno = EBADF;
69746366Sbostic 	    hashp->errno = errno;
69846366Sbostic 	    return ( ERROR );
69946366Sbostic 	}
70046366Sbostic #ifdef HASH_STATISTICS
70146366Sbostic 	hash_accesses++;
70246366Sbostic #endif
70346366Sbostic 
70446366Sbostic 	if ( (hashp->cbucket < 0) || (flag == R_FIRST) ) {
70546366Sbostic 	    hashp->cbucket = 0;
70646366Sbostic 	    hashp->cndx = 1;
70746366Sbostic 	    hashp->cpage = NULL;
70846366Sbostic 	}
70946366Sbostic 
71046366Sbostic 	if ( !(bufp = hashp->cpage) ) {
71146366Sbostic 	    for (bucket = hashp->cbucket;
71246366Sbostic 		 bucket <= hashp->MAX_BUCKET;
71346366Sbostic 		 bucket++, hashp->cndx = 1 ) {
71446366Sbostic 
71546366Sbostic 		bufp = __get_buf ( bucket, NULL, 0 );
71646366Sbostic 		if (!bufp) return(ERROR);
71746366Sbostic 		hashp->cpage = bufp;
71846366Sbostic 		bp = (u_short *)bufp->page;
71946366Sbostic 		if (bp[0]) break;
72046366Sbostic 	    }
72146366Sbostic 	    hashp->cbucket = bucket;
72246366Sbostic 	    if ( hashp->cbucket > hashp->MAX_BUCKET ) {
72346366Sbostic 		hashp->cbucket = -1;
72446366Sbostic 		return ( ABNORMAL );
72546366Sbostic 	    }
72646366Sbostic 	} else {
72746366Sbostic 	    bp  = (u_short *)hashp->cpage->page;
72846366Sbostic 	}
72946366Sbostic 
73046366Sbostic #ifdef DEBUG
73146366Sbostic 	assert (bp);
73246366Sbostic 	assert (bufp);
73346366Sbostic #endif
73446366Sbostic 	while ( bp[hashp->cndx+1] == OVFLPAGE ) {
73546366Sbostic 	    bufp = hashp->cpage = __get_buf ( bp[hashp->cndx], bufp, 0 );
73646366Sbostic 	    if (!bufp) return(ERROR);
73746366Sbostic 	    bp = (u_short *)(bufp->page);
73846366Sbostic 	    hashp->cndx = 1;
73946366Sbostic 	}
74046366Sbostic 	ndx = hashp->cndx;
74146366Sbostic 	if (bp[ndx+1] < REAL_KEY) {
74246366Sbostic 	    if (__big_keydata(bufp, ndx, key, data, 1)) {
74346366Sbostic 		return(ERROR);
74446366Sbostic 	    }
74546366Sbostic 	} else {
74646366Sbostic 	    key->data = hashp->cpage->page + bp[ndx];
74746366Sbostic 	    key->size = (ndx > 1 ? bp[ndx-1] : hashp->BSIZE) - bp[ndx];
74846366Sbostic 	    data->data = hashp->cpage->page + bp[ndx + 1];
74946366Sbostic 	    data->size = bp[ndx] - bp[ndx + 1];
75046366Sbostic 	    ndx += 2;
75146366Sbostic 	    if ( ndx > bp[0] ) {
75246366Sbostic 		hashp->cpage = NULL;
75346366Sbostic 		hashp->cbucket++;
75446366Sbostic 		hashp->cndx=1;
75546366Sbostic 	    } else hashp->cndx = ndx;
75646366Sbostic 	}
75746366Sbostic 	return (SUCCESS);
75846366Sbostic }
75946366Sbostic 
76046366Sbostic /********************************* UTILITIES ************************/
76146366Sbostic /*
76246366Sbostic     0 ==> OK
76346366Sbostic     -1 ==> Error
76446366Sbostic */
76546366Sbostic extern int
76646366Sbostic __expand_table()
76746366Sbostic {
76846366Sbostic 	int	old_bucket, new_bucket;
76946366Sbostic 	int	new_segnum;
77046366Sbostic 	int	dirsize;
77146366Sbostic 	int	spare_ndx;
77246366Sbostic 	register	char **old, **new;
77346366Sbostic #ifdef HASH_STATISTICS
77446366Sbostic 	hash_expansions++;
77546366Sbostic #endif
77646366Sbostic 
77746366Sbostic 	new_bucket = ++hashp->MAX_BUCKET;
77846366Sbostic 	old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK);
77946366Sbostic 
78046366Sbostic 	new_segnum = new_bucket >> hashp->SSHIFT;
78146366Sbostic 
78246366Sbostic 	/* Check if we need a new segment */
78346366Sbostic 	if ( new_segnum >= hashp->nsegs ) {
78446366Sbostic 
78546366Sbostic 	    /* Check if we need to expand directory */
78646366Sbostic 	    if ( new_segnum >= hashp->DSIZE ) {
78746366Sbostic 
78846366Sbostic 		/* Reallocate directory */
78946366Sbostic 		dirsize = hashp->DSIZE * sizeof ( SEGMENT * );
79046366Sbostic 		if (!hash_realloc ( &hashp->dir, dirsize, (dirsize << 1 ) )) {
79146366Sbostic 		    return(-1);
79246366Sbostic 		}
79346366Sbostic 		hashp->DSIZE = dirsize << 1;
79446366Sbostic 	    }
79546366Sbostic 	    if (!(hashp->dir[new_segnum] =
79646366Sbostic 		    (SEGMENT)calloc ( hashp->SGSIZE, sizeof(SEGMENT)))) {
79746366Sbostic 		    return(-1);
79846366Sbostic 	    }
79946366Sbostic 	    hashp->exsegs++;
80046366Sbostic 	    hashp->nsegs++;
80146366Sbostic 	}
80246366Sbostic 
80346366Sbostic 	/*
80446366Sbostic 	    If the split point is increasing (MAX_BUCKET's log
80546366Sbostic 	    base 2 increases), we need to copy the current contents
80646366Sbostic 	    of the spare split bucket to the next bucket
80746366Sbostic 	*/
80846366Sbostic 	spare_ndx = __log2(hashp->MAX_BUCKET);
80946366Sbostic 	if ( spare_ndx != (__log2(hashp->MAX_BUCKET - 1))) {
81046366Sbostic 	    hashp->SPARES[spare_ndx] = hashp->SPARES[spare_ndx-1];
81146366Sbostic 	}
81246366Sbostic 
81346366Sbostic 	if ( new_bucket > hashp->HIGH_MASK ) {
81446366Sbostic 	    /* Starting a new doubling */
81546366Sbostic 	    hashp->LOW_MASK = hashp->HIGH_MASK;
81646366Sbostic 	    hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK;
81746366Sbostic 	}
81846366Sbostic 
81946366Sbostic 	/*
82046366Sbostic 	 * Relocate records to the new bucket
82146366Sbostic 	 */
82246366Sbostic 	return(__split_page ( old_bucket, new_bucket ));
82346366Sbostic }
82446366Sbostic /*
82546366Sbostic     If realloc guarantees that the pointer is not destroyed
82646366Sbostic     if the realloc fails, then this routine can go away
82746366Sbostic */
82846366Sbostic static char *
82946366Sbostic hash_realloc ( p_ptr, oldsize, newsize )
83046366Sbostic char	**p_ptr;
83146366Sbostic int	oldsize;
83246366Sbostic int	newsize;
83346366Sbostic {
83446366Sbostic 	register char	*p;
83546366Sbostic 
83646366Sbostic 	if (p = (char *)malloc ( newsize ) ) {
83746366Sbostic 		bcopy ( *p_ptr, p, oldsize );
83846366Sbostic 		bzero ( *p_ptr + oldsize, newsize-oldsize );
83946366Sbostic 		free(*p_ptr);
84046366Sbostic 		*p_ptr = p;
84146366Sbostic 	}
84246366Sbostic 	return (p);
84346366Sbostic }
84446366Sbostic 
84546366Sbostic extern int
84646366Sbostic __call_hash ( k, len )
84746366Sbostic char	*k;
84846366Sbostic int	len;
84946366Sbostic {
85046366Sbostic 	int	n, bucket;
85146366Sbostic 	n = hashp->hash(k, len);
85246366Sbostic 
85346366Sbostic 	bucket = n & hashp->HIGH_MASK;
85446366Sbostic 	if ( bucket > hashp->MAX_BUCKET ) {
85546366Sbostic 	    bucket = bucket & hashp->LOW_MASK;
85646366Sbostic 	}
85746366Sbostic 
85846366Sbostic 	return(bucket);
85946366Sbostic }
86046366Sbostic 
86146366Sbostic /*
86246366Sbostic     Allocate segment table.  On error, destroy the table
86346366Sbostic     and set errno.
86446366Sbostic 
86546366Sbostic     Returns 0 on success
86646366Sbostic */
86746366Sbostic static int
86846366Sbostic alloc_segs ( nsegs )
86946366Sbostic int	nsegs;
87046366Sbostic {
87146366Sbostic     register int	i;
87246366Sbostic     register SEGMENT	store;
87346366Sbostic 
87446366Sbostic     int	save_errno;
87546366Sbostic 
87646366Sbostic     if (!(hashp->dir = (SEGMENT *)calloc(hashp->DSIZE, sizeof(SEGMENT *)))) {
87746366Sbostic 	save_errno = errno;
87846366Sbostic 	(void)hdestroy();
87946366Sbostic 	errno = save_errno;
88046366Sbostic 	return(-1);
88146366Sbostic     }
88246366Sbostic 
88346366Sbostic     /* Allocate segments */
88446366Sbostic     store = (SEGMENT)calloc ( nsegs << hashp->SSHIFT, sizeof (SEGMENT) );
88546366Sbostic     if ( !store ) {
88646366Sbostic 	save_errno = errno;
88746366Sbostic 	(void)hdestroy();
88846366Sbostic 	errno = save_errno;
88946366Sbostic 	return(-1);
89046366Sbostic     }
89146366Sbostic 
89246366Sbostic     for ( i=0; i < nsegs; i++, hashp->nsegs++ ) {
89346366Sbostic 	hashp->dir[i] = &store[i<<hashp->SSHIFT];
89446366Sbostic     }
89546366Sbostic     return(0);
89646366Sbostic }
89746366Sbostic 
89846366Sbostic /*
89946366Sbostic     Hashp->hdr needs to be byteswapped.
90046366Sbostic */
90146366Sbostic static void
90246366Sbostic swap_header_copy ( srcp, destp )
90346366Sbostic HASHHDR	*srcp;
90446366Sbostic HASHHDR	*destp;
90546366Sbostic {
90646366Sbostic     int i;
90746366Sbostic 
90846366Sbostic     BLSWAP_COPY(srcp->magic, destp->magic);
90946366Sbostic     BLSWAP_COPY(srcp->version, destp->version);
91046366Sbostic     BLSWAP_COPY(srcp->lorder, destp->lorder);
91146366Sbostic     BLSWAP_COPY(srcp->bsize, destp->bsize);
91246366Sbostic     BLSWAP_COPY(srcp->bshift, destp->bshift);
91346366Sbostic     BLSWAP_COPY(srcp->dsize, destp->dsize);
91446366Sbostic     BLSWAP_COPY(srcp->ssize, destp->ssize);
91546366Sbostic     BLSWAP_COPY(srcp->sshift, destp->sshift);
91646366Sbostic     BLSWAP_COPY(srcp->max_bucket, destp->max_bucket);
91746366Sbostic     BLSWAP_COPY(srcp->high_mask, destp->high_mask);
91846366Sbostic     BLSWAP_COPY(srcp->low_mask, destp->low_mask);
91946366Sbostic     BLSWAP_COPY(srcp->ffactor, destp->ffactor);
92046366Sbostic     BLSWAP_COPY(srcp->nkeys, destp->nkeys);
92146366Sbostic     BLSWAP_COPY(srcp->hdrpages, destp->hdrpages);
92246366Sbostic     BLSWAP_COPY(srcp->h_charkey, destp->h_charkey);
92346366Sbostic     for ( i=0; i < NCACHED; i++ ) {
92446366Sbostic 	BLSWAP_COPY ( srcp->spares[i] , destp->spares[i]);
92546366Sbostic 	BSSWAP_COPY ( srcp->bitmaps[i] , destp->bitmaps[i]);
92646366Sbostic     }
92746366Sbostic     return;
92846366Sbostic }
92946366Sbostic 
93046366Sbostic static void
93146366Sbostic swap_header ( )
93246366Sbostic {
93346366Sbostic     HASHHDR	*hdrp;
93446366Sbostic     int	i;
93546366Sbostic 
93646366Sbostic     hdrp = &hashp->hdr;
93746366Sbostic 
93846366Sbostic     BLSWAP(hdrp->magic);
93946366Sbostic     BLSWAP(hdrp->version);
94046366Sbostic     BLSWAP(hdrp->lorder);
94146366Sbostic     BLSWAP(hdrp->bsize);
94246366Sbostic     BLSWAP(hdrp->bshift);
94346366Sbostic     BLSWAP(hdrp->dsize);
94446366Sbostic     BLSWAP(hdrp->ssize);
94546366Sbostic     BLSWAP(hdrp->sshift);
94646366Sbostic     BLSWAP(hdrp->max_bucket);
94746366Sbostic     BLSWAP(hdrp->high_mask);
94846366Sbostic     BLSWAP(hdrp->low_mask);
94946366Sbostic     BLSWAP(hdrp->ffactor);
95046366Sbostic     BLSWAP(hdrp->nkeys);
95146366Sbostic     BLSWAP(hdrp->hdrpages);
95246366Sbostic     BLSWAP(hdrp->h_charkey);
95346366Sbostic     for ( i=0; i < NCACHED; i++ ) {
95446366Sbostic 	BLSWAP ( hdrp->spares[i] );
95546366Sbostic 	BSSWAP ( hdrp->bitmaps[i] );
95646366Sbostic     }
95746366Sbostic     return;
95846366Sbostic }
959