xref: /csrg-svn/lib/libc/db/hash/hash.c (revision 51072)
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*51072Sbostic static char sccsid[] = "@(#)hash.c	5.18 (Berkeley) 09/11/91";
1346366Sbostic #endif /* LIBC_SCCS and not lint */
1446366Sbostic 
1546366Sbostic #include <sys/param.h>
1646366Sbostic #include <sys/stat.h>
1746562Sbostic #include <fcntl.h>
1846366Sbostic #include <errno.h>
1950997Sbostic #ifdef DEBUG
2046366Sbostic #include <assert.h>
2150997Sbostic #endif
2246366Sbostic #include <db.h>
2346562Sbostic #include <stdio.h>
2446562Sbostic #include <stdlib.h>
2546500Sbostic #include <unistd.h>
2646562Sbostic #include <string.h>
2746366Sbostic #include "hash.h"
2850997Sbostic #include "page.h"
2950997Sbostic #include "extern.h"
3046366Sbostic 
3150997Sbostic static int   alloc_segs __P((int));
3250997Sbostic static int   flush_meta __P((void));
3350997Sbostic static int   hash_access __P((ACTION, DBT *, DBT *));
3450997Sbostic static int   hash_close __P((DB *));
3550997Sbostic static int   hash_delete __P((const DB *, const DBT *, u_int));
3651057Sbostic static int   hash_get __P((const DB *, const DBT *, DBT *, u_int));
3750997Sbostic static int   hash_put __P((const DB *, const DBT *, const DBT *, u_int));
3850997Sbostic static void *hash_realloc __P((SEGMENT **, int, int));
3950997Sbostic static int   hash_seq __P((const DB *, DBT *, DBT *, u_int));
4050997Sbostic static int   hash_sync __P((const DB *));
4150997Sbostic static int   hdestroy __P((void));
4250997Sbostic static HTAB *init_hash __P((HASHINFO *));
4350997Sbostic static int   init_htab __P((int));
4450997Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
4550997Sbostic static void  swap_header __P((void));
4650997Sbostic static void  swap_header_copy __P((HASHHDR *, HASHHDR *));
4750997Sbostic #endif
4850997Sbostic 
4946366Sbostic /* Fast arithmetic, relying on powers of 2, */
5050997Sbostic #define MOD(x, y)		((x) & ((y) - 1))
5146366Sbostic 
5250997Sbostic #define RETURN_ERROR(ERR, LOC)	{ save_errno = ERR; goto LOC; }
5346366Sbostic 
5446366Sbostic /* Return values */
5550997Sbostic #define	SUCCESS	 (0)
5650997Sbostic #define	ERROR	(-1)
5750997Sbostic #define	ABNORMAL (1)
5846366Sbostic 
5946366Sbostic /* Local data */
6046366Sbostic HTAB *hashp = NULL;
6146366Sbostic 
6246366Sbostic #ifdef HASH_STATISTICS
6346366Sbostic long hash_accesses, hash_collisions, hash_expansions, hash_overflows;
6446366Sbostic #endif
6546366Sbostic 
6650997Sbostic /************************** INTERFACE ROUTINES ***************************/
6746366Sbostic /* OPEN/CLOSE */
6846366Sbostic 
6950997Sbostic extern DB *
70*51072Sbostic __hash_open(file, flags, mode, info)
7150997Sbostic 	const char *file;
7250997Sbostic 	int flags, mode;
7350997Sbostic 	const HASHINFO *info;	/* Special directives for create */
7446366Sbostic {
7550997Sbostic 	struct stat statbuf;
7650997Sbostic 	DB *dbp;
7750997Sbostic 	int bpages, hdrsize, new_table, nsegs, save_errno;
7846366Sbostic 
7950997Sbostic 	if (!(hashp = calloc(1, sizeof(HTAB))))
8050997Sbostic 		return (NULL);
8150997Sbostic 	hashp->fp = -1;
8250997Sbostic 	/*
8350997Sbostic 	 * Select flags relevant to us. Even if user wants write only, we need
8450997Sbostic 	 * to be able to read the actual file, so we need to open it read/write.
8550997Sbostic 	 * But, the field in the hashp structure needs to be accurate so that
8650997Sbostic 	 * we can check accesses.
8750997Sbostic 	 */
8850997Sbostic 	hashp->flags = flags =
8950997Sbostic 	    flags & (O_CREAT | O_EXCL | O_RDONLY | O_RDWR | O_TRUNC | O_WRONLY);
9050997Sbostic 	if (flags & O_WRONLY)
9150997Sbostic 		flags = (flags & ~O_WRONLY) | O_RDWR;
9246366Sbostic 
9350997Sbostic 	new_table = 0;
9450997Sbostic 	if (!file || (flags & O_TRUNC) ||
9550997Sbostic 	    (stat(file, &statbuf) && (errno == ENOENT))) {
9650997Sbostic 		if (errno == ENOENT)
9750997Sbostic 			errno = 0; /* Just in case someone looks at errno */
9850997Sbostic 		new_table = 1;
9946485Sbostic 	}
10050997Sbostic 	if (file) {
10150997Sbostic 		if ((hashp->fp = open(file, flags, mode)) == -1)
10250997Sbostic 			RETURN_ERROR(errno, error0);
10350997Sbostic 		(void)fcntl(hashp->fp, F_SETFD, 1);
10446366Sbostic 	}
10550997Sbostic 	if (new_table) {
10650997Sbostic 		if (!(hashp = init_hash((HASHINFO *)info)))
10750997Sbostic 			RETURN_ERROR(errno, error1);
10850997Sbostic 	} else {
10950997Sbostic 		/* Table already exists */
11050997Sbostic 		if (info && info->hash)
11150997Sbostic 			hashp->hash = info->hash;
11250997Sbostic 		else
11350997Sbostic 			hashp->hash = default_hash;
11446366Sbostic 
11550997Sbostic 		hdrsize = read(hashp->fp, &hashp->hdr, sizeof(HASHHDR));
11646366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
11750997Sbostic 		swap_header();
11846366Sbostic #endif
11950997Sbostic 		if (hdrsize == -1)
12050997Sbostic 			RETURN_ERROR(errno, error1);
12150997Sbostic 		if (hdrsize != sizeof(HASHHDR))
12250997Sbostic 			RETURN_ERROR(EFTYPE, error1);
12350997Sbostic 		/* Verify file type, versions and hash function */
12450997Sbostic 		if (hashp->MAGIC != HASHMAGIC)
12550997Sbostic 			RETURN_ERROR(EFTYPE, error1);
12651061Sbostic 		if (hashp->VERSION != HASHVERSION)
12750997Sbostic 			RETURN_ERROR(EFTYPE, error1);
12850997Sbostic 		if (hashp->hash(CHARKEY, sizeof(CHARKEY)) != hashp->H_CHARKEY)
12950997Sbostic 			RETURN_ERROR(EFTYPE, error1);
13051057Sbostic 		/*
13151057Sbostic 		 * Figure out how many segments we need.  Max_Bucket is the
13251057Sbostic 		 * maximum bucket number, so the number of buckets is
13351057Sbostic 		 * max_bucket + 1.
13451057Sbostic 		 */
13551057Sbostic 		nsegs = (hashp->MAX_BUCKET + 1 + hashp->SGSIZE - 1) /
13650997Sbostic 			 hashp->SGSIZE;
13750997Sbostic 		hashp->nsegs = 0;
13850997Sbostic 		if (alloc_segs(nsegs))
13950997Sbostic 			/*
14050997Sbostic 			 * If alloc_segs fails, table will have been destroyed
14150997Sbostic 			 * and errno will have been set.
14250997Sbostic 			 */
14350997Sbostic 			return (NULL);
14450997Sbostic 		/* Read in bitmaps */
14551061Sbostic 		bpages = (hashp->SPARES[hashp->OVFL_POINT] +
14650997Sbostic 		    (hashp->BSIZE << BYTE_SHIFT) - 1) >>
14750997Sbostic 		    (hashp->BSHIFT + BYTE_SHIFT);
14846366Sbostic 
14950997Sbostic 		hashp->nmaps = bpages;
15051057Sbostic 		(void)memset(&hashp->mapp[0], 0, bpages * sizeof(u_long *));
15146366Sbostic 	}
15246366Sbostic 
15350997Sbostic 	/* Initialize Buffer Manager */
15450997Sbostic 	if (info && info->cachesize)
15550997Sbostic 		__buf_init(info->cachesize);
15650997Sbostic 	else
15750997Sbostic 		__buf_init(DEF_BUFSIZE);
15850997Sbostic 
15950997Sbostic 	hashp->new_file = new_table;
16050997Sbostic 	hashp->save_file = file && (hashp->flags & (O_WRONLY | O_RDWR));
16150997Sbostic 	hashp->cbucket = -1;
16250997Sbostic 	if (!(dbp = malloc(sizeof(DB)))) {
16350997Sbostic 		save_errno = errno;
16450997Sbostic 		hdestroy();
16550997Sbostic 		errno = save_errno;
16650997Sbostic 		return (NULL);
16746366Sbostic 	}
16850997Sbostic 	dbp->internal = (char *)hashp;
16950997Sbostic 	dbp->close = hash_close;
17050997Sbostic 	dbp->del = hash_delete;
17150997Sbostic 	dbp->get = hash_get;
17250997Sbostic 	dbp->put = hash_put;
17350997Sbostic 	dbp->seq = hash_seq;
17450997Sbostic 	dbp->sync = hash_sync;
17550997Sbostic 	dbp->type = DB_HASH;
17646366Sbostic 
17746366Sbostic #ifdef DEBUG
17850997Sbostic 	(void)fprintf(stderr,
17951061Sbostic "%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%d\n%s%d\n%s%x\n%s%x\n%s%d\n%s%d\n",
18050997Sbostic 	    "init_htab:",
18150997Sbostic 	    "TABLE POINTER   ", hashp,
18250997Sbostic 	    "BUCKET SIZE     ", hashp->BSIZE,
18350997Sbostic 	    "BUCKET SHIFT    ", hashp->BSHIFT,
18450997Sbostic 	    "DIRECTORY SIZE  ", hashp->DSIZE,
18550997Sbostic 	    "SEGMENT SIZE    ", hashp->SGSIZE,
18650997Sbostic 	    "SEGMENT SHIFT   ", hashp->SSHIFT,
18750997Sbostic 	    "FILL FACTOR     ", hashp->FFACTOR,
18850997Sbostic 	    "MAX BUCKET      ", hashp->MAX_BUCKET,
18951061Sbostic 	    "OVFL POINT	     ", hashp->OVFL_POINT,
19051061Sbostic 	    "LAST FREED      ", hashp->LAST_FREED,
19150997Sbostic 	    "HIGH MASK       ", hashp->HIGH_MASK,
19250997Sbostic 	    "LOW  MASK       ", hashp->LOW_MASK,
19350997Sbostic 	    "NSEGS           ", hashp->nsegs,
19450997Sbostic 	    "NKEYS           ", hashp->NKEYS);
19546366Sbostic #endif
19646366Sbostic #ifdef HASH_STATISTICS
19746366Sbostic 	hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0;
19846366Sbostic #endif
19950997Sbostic 	return (dbp);
20046366Sbostic 
20146366Sbostic error1:
20250997Sbostic 	(void)close(hashp->fp);
20346366Sbostic 
20446366Sbostic error0:
20550997Sbostic 	free(hashp);
20650997Sbostic 	errno = save_errno;
20750997Sbostic 	return (NULL);
20846366Sbostic }
20946366Sbostic 
21046366Sbostic static int
21150997Sbostic hash_close(dbp)
21250997Sbostic 	DB *dbp;
21346366Sbostic {
21450997Sbostic 	int retval;
21546457Sbostic 
21650997Sbostic 	if (!dbp)
21750997Sbostic 		return (ERROR);
21846366Sbostic 	hashp = (HTAB *)dbp->internal;
21946457Sbostic 	retval = hdestroy();
22050997Sbostic 	free(dbp);
22150997Sbostic 	return (retval);
22246366Sbostic }
22346366Sbostic 
22446366Sbostic /************************** LOCAL CREATION ROUTINES **********************/
22550997Sbostic static HTAB *
22650997Sbostic init_hash(info)
22750997Sbostic 	HASHINFO *info;
22846366Sbostic {
22950997Sbostic 	int nelem;
23046366Sbostic 
23146366Sbostic 	nelem = 1;
23251061Sbostic 	hashp->NKEYS = 0;
23350997Sbostic 	hashp->LORDER = BYTE_ORDER;
23450997Sbostic 	hashp->BSIZE = DEF_BUCKET_SIZE;
23550997Sbostic 	hashp->BSHIFT = DEF_BUCKET_SHIFT;
23650997Sbostic 	hashp->SGSIZE = DEF_SEGSIZE;
23750997Sbostic 	hashp->SSHIFT = DEF_SEGSIZE_SHIFT;
23850997Sbostic 	hashp->DSIZE = DEF_DIRSIZE;
23950997Sbostic 	hashp->FFACTOR = DEF_FFACTOR;
24050997Sbostic 	hashp->hash = default_hash;
24150997Sbostic 	bzero(hashp->SPARES, sizeof(hashp->SPARES));
24251061Sbostic 	bzero (hashp->BITMAPS, sizeof (hashp->BITMAPS));
24346366Sbostic 
24450997Sbostic 	if (info) {
24550997Sbostic 		if (info->bsize) {
24650997Sbostic 			/* Round pagesize up to power of 2 */
24750997Sbostic 			hashp->BSHIFT = __log2(info->bsize);
24850997Sbostic 			hashp->BSIZE = 1 << hashp->BSHIFT;
24950997Sbostic 			if (hashp->BSIZE > MAX_BSIZE) {
25050997Sbostic 				errno = EINVAL;
25150997Sbostic 				return (NULL);
25250997Sbostic 			}
25346366Sbostic 		}
25450997Sbostic 		if (info->ffactor)
25550997Sbostic 			hashp->FFACTOR = info->ffactor;
25650997Sbostic 		if (info->hash)
25750997Sbostic 			hashp->hash = info->hash;
25850997Sbostic 		if (info->nelem)
25950997Sbostic 			nelem = info->nelem;
26050997Sbostic 		if (info->lorder) {
26150997Sbostic 			if (info->lorder != BIG_ENDIAN &&
26250997Sbostic 			    info->lorder != LITTLE_ENDIAN) {
26350997Sbostic 				errno = EINVAL;
26450997Sbostic 				return (NULL);
26550997Sbostic 			}
26650997Sbostic 			hashp->LORDER = info->lorder;
26746366Sbostic 		}
26846366Sbostic 	}
26946366Sbostic 	/* init_htab should destroy the table and set errno if it fails */
27050997Sbostic 	if (init_htab(nelem))
27150997Sbostic 		return (NULL);
27250997Sbostic 	else
27350997Sbostic 		return (hashp);
27446366Sbostic }
27546366Sbostic /*
27650997Sbostic  * This calls alloc_segs which may run out of memory.  Alloc_segs will destroy
27750997Sbostic  * the table and set errno, so we just pass the error information along.
27850997Sbostic  *
27950997Sbostic  * Returns 0 on No Error
28050997Sbostic  */
28146366Sbostic static int
28250997Sbostic init_htab(nelem)
28350997Sbostic 	int nelem;
28446366Sbostic {
28550997Sbostic 	register int nbuckets, nsegs;
28650997Sbostic 	int l2;
28746366Sbostic 
28850997Sbostic 	/*
28950997Sbostic 	 * Divide number of elements by the fill factor and determine a
29050997Sbostic 	 * desired number of buckets.  Allocate space for the next greater
29150997Sbostic 	 * power of two number of buckets.
29250997Sbostic 	 */
29346366Sbostic 	nelem = (nelem - 1) / hashp->FFACTOR + 1;
29446366Sbostic 
29546366Sbostic 	l2 = __log2(nelem);
29646366Sbostic 	nbuckets = 1 << l2;
29750997Sbostic 	nbuckets = MAX(nbuckets, 2);
29846366Sbostic 
29946366Sbostic 	hashp->SPARES[l2] = l2 + 1;
30050997Sbostic 	hashp->SPARES[l2 + 1] = l2 + 1;
30151061Sbostic 	hashp->OVFL_POINT = l2;
30251061Sbostic 	hashp->LAST_FREED = 2;
30351061Sbostic 
30450997Sbostic 	/* First bitmap page is at: splitpoint l2 page offset 1 */
30551057Sbostic 	if (__init_bitmap(OADDR_OF(l2, 1), l2 + 1, 0))
30651057Sbostic 		return (-1);
30746366Sbostic 
30846366Sbostic 	hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1;
30946366Sbostic 	hashp->HIGH_MASK = (nbuckets << 1) - 1;
31050997Sbostic 	hashp->HDRPAGES = ((MAX(sizeof(HASHHDR), MINHDRSIZE) - 1) >>
31150997Sbostic 	    hashp->BSHIFT) + 1;
31246366Sbostic 
31346366Sbostic 	nsegs = (nbuckets - 1) / hashp->SGSIZE + 1;
31446366Sbostic 	nsegs = 1 << __log2(nsegs);
31546366Sbostic 
31650997Sbostic 	if (nsegs > hashp->DSIZE)
31750997Sbostic 		hashp->DSIZE = nsegs;
31850997Sbostic 	return (alloc_segs(nsegs));
31946366Sbostic }
32046366Sbostic 
32146366Sbostic /********************** DESTROY/CLOSE ROUTINES ************************/
32246366Sbostic 
32346366Sbostic /*
32450997Sbostic  * Flushes any changes to the file if necessary and destroys the hashp
32550997Sbostic  * structure, freeing all allocated space.
32650997Sbostic  */
32746366Sbostic static int
32846366Sbostic hdestroy()
32946366Sbostic {
33050997Sbostic 	int i, save_errno;
33146366Sbostic 
33246366Sbostic 	save_errno = 0;
33346366Sbostic 
33446366Sbostic 	if (hashp != NULL) {
33546366Sbostic #ifdef HASH_STATISTICS
33650997Sbostic 		(void)fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n",
33750997Sbostic 		    hash_accesses, hash_collisions);
33850997Sbostic 		(void)fprintf(stderr, "hdestroy: expansions %ld\n",
33950997Sbostic 		    hash_expansions);
34050997Sbostic 		(void)fprintf(stderr, "hdestroy: overflows %ld\n",
34150997Sbostic 		    hash_overflows);
34250997Sbostic 		(void)fprintf(stderr, "keys %ld maxp %d segmentcount %d\n",
34350997Sbostic 		    hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs);
34446366Sbostic 
34550997Sbostic 		for (i = 0; i < NCACHED; i++)
34650997Sbostic 			(void)fprintf(stderr,
34750997Sbostic 			    "spares[%d] = %d\n", i, hashp->SPARES[i]);
34846366Sbostic #endif
34950997Sbostic 		/*
35050997Sbostic 		 * Call on buffer manager to free buffers, and if required,
35150997Sbostic 		 * write them to disk.
35250997Sbostic 		 */
35350997Sbostic 		if (__buf_free(1, hashp->save_file))
35450997Sbostic 			save_errno = errno;
35550997Sbostic 		if (hashp->dir) {
35650997Sbostic 			free(*hashp->dir);	/* Free initial segments */
35750997Sbostic 			/* Free extra segments */
35850997Sbostic 			while (hashp->exsegs--)
35950997Sbostic 				free(hashp->dir[--hashp->nsegs]);
36050997Sbostic 			free(hashp->dir);
36146366Sbostic 		}
36250997Sbostic 		if (flush_meta() && !save_errno)
36350997Sbostic 			save_errno = errno;
36447251Sbostic 		/* Free Bigmaps */
36550997Sbostic 		for (i = 0; i < hashp->nmaps; i++)
36650997Sbostic 			if (hashp->mapp[i])
36750997Sbostic 				free(hashp->mapp[i]);
36846503Sbostic 
36950997Sbostic 		if (hashp->fp != -1)
37050997Sbostic 			(void)close(hashp->fp);
37150997Sbostic 		free(hashp);
37246366Sbostic 		hashp = NULL;
37346366Sbostic 	}
37450997Sbostic 	if (save_errno) {
37550997Sbostic 		errno = save_errno;
37650997Sbostic 		return (ERROR);
37746366Sbostic 	}
37850997Sbostic 	return (SUCCESS);
37946366Sbostic }
38050997Sbostic /*
38150997Sbostic  * Write modified pages to disk
38250997Sbostic  *
38350997Sbostic  * Returns:
38450997Sbostic  *	 0 == OK
38550997Sbostic  *	-1 ERROR
38650997Sbostic  */
38746366Sbostic static int
38846366Sbostic hash_sync(dbp)
38950997Sbostic 	const DB *dbp;
39046366Sbostic {
39150997Sbostic 	if (!dbp)
39250997Sbostic 		return (ERROR);
39346366Sbostic 	hashp = (HTAB *)dbp->internal;
39446366Sbostic 
39550997Sbostic 	if (!hashp->save_file)
39650997Sbostic 		return (0);
39750997Sbostic 	if (__buf_free(0, 1) || flush_meta())
39850997Sbostic 		return (ERROR);
39946366Sbostic 	hashp->new_file = 0;
40050997Sbostic 	return (0);
40146366Sbostic }
40246366Sbostic 
40346366Sbostic /*
40450997Sbostic  * Returns:
40550997Sbostic  *	 0 == OK
40650997Sbostic  *	-1 indicates that errno should be set
40750997Sbostic  */
40846366Sbostic static int
40946366Sbostic flush_meta()
41046366Sbostic {
41150997Sbostic 	HASHHDR *whdrp;
41250997Sbostic 	int fp, i, wsize;
41346366Sbostic 
41450997Sbostic 	if (!hashp->save_file)
41550997Sbostic 		return (0);
41646366Sbostic 	hashp->MAGIC = HASHMAGIC;
41751061Sbostic 	hashp->VERSION = HASHVERSION;
41850997Sbostic 	hashp->H_CHARKEY = hashp->hash(CHARKEY, sizeof(CHARKEY));
41946366Sbostic 
42046366Sbostic 	fp = hashp->fp;
42146366Sbostic 	whdrp = &hashp->hdr;
42246366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
42346366Sbostic 	whdrp = &whdr;
42450997Sbostic 	swap_header_copy(&hashp->hdr, whdrp);
42546366Sbostic #endif
42650997Sbostic 	if ((lseek(fp, 0, SEEK_SET) == -1) ||
42750997Sbostic 	    ((wsize = write(fp, whdrp, sizeof(HASHHDR))) == -1))
42850997Sbostic 		return (-1);
42950997Sbostic 	else
43050997Sbostic 		if (wsize != sizeof(HASHHDR)) {
43150997Sbostic 			errno = EFTYPE;
43250997Sbostic 			hashp->errno = errno;
43350997Sbostic 			return (-1);
43446366Sbostic 		}
43550997Sbostic 	for (i = 0; i < NCACHED; i++)
43650997Sbostic 		if (hashp->mapp[i])
437*51072Sbostic 			if (__put_page((char *)hashp->mapp[i],
43850997Sbostic 				hashp->BITMAPS[i], 0, 1))
43950997Sbostic 				return (-1);
44050997Sbostic 	return (0);
44146366Sbostic }
44250997Sbostic 
44346366Sbostic /*******************************SEARCH ROUTINES *****************************/
44446366Sbostic /*
44550997Sbostic  * All the access routines return
44650997Sbostic  *
44750997Sbostic  * Returns:
44850997Sbostic  *	 0 on SUCCESS
44950997Sbostic  *	 1 to indicate an external ERROR (i.e. key not found, etc)
45050997Sbostic  *	-1 to indicate an internal ERROR (i.e. out of memory, etc)
45150997Sbostic  */
45246366Sbostic static int
45350997Sbostic hash_get(dbp, key, data, flag)
45450997Sbostic 	const DB *dbp;
45551057Sbostic 	const DBT *key;
45651057Sbostic 	DBT *data;
45750997Sbostic 	u_int flag;
45846366Sbostic {
45950997Sbostic 	if (flag) {
46050997Sbostic 		hashp->errno = errno = EINVAL;
46150997Sbostic 		return (ERROR);
46250997Sbostic 	}
46350997Sbostic 	hashp = (HTAB *)dbp->internal;
46450997Sbostic 	if (hashp->flags & O_WRONLY) {
46550997Sbostic 		hashp->errno = errno = EPERM;
46650997Sbostic 		return (ERROR);
46750997Sbostic 	}
46851057Sbostic 	return (hash_access(HASH_GET, (DBT *)key, data));
46946366Sbostic }
47046366Sbostic 
47146366Sbostic static int
47250997Sbostic hash_put(dbp, key, data, flag)
47350997Sbostic 	const DB *dbp;
47450997Sbostic 	const DBT *key, *data;
47550997Sbostic 	u_int flag;
47646366Sbostic {
47750997Sbostic 	if (flag && flag != R_NOOVERWRITE) {
47850997Sbostic 		hashp->errno = errno = EINVAL;
47950997Sbostic 		return (ERROR);
48050997Sbostic 	}
48150997Sbostic 	hashp = (HTAB *)dbp->internal;
48250997Sbostic 	if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
48350997Sbostic 		hashp->errno = errno = EPERM;
48450997Sbostic 		return (ERROR);
48550997Sbostic 	}
48651057Sbostic 	return (hash_access(flag == R_NOOVERWRITE ?
48750997Sbostic 	    HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data));
48846366Sbostic }
48946366Sbostic 
49046366Sbostic static int
49150997Sbostic hash_delete(dbp, key, flag)
49250997Sbostic 	const DB *dbp;
49350997Sbostic 	const DBT *key;
49450997Sbostic 	u_int flag;		/* Ignored */
49546366Sbostic {
49650997Sbostic 	if (flag && flag != R_CURSOR) {
49750997Sbostic 		hashp->errno = errno = EINVAL;
49850997Sbostic 		return (ERROR);
49950997Sbostic 	}
50050997Sbostic 	hashp = (HTAB *)dbp->internal;
50150997Sbostic 	if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
50250997Sbostic 		hashp->errno = errno = EPERM;
50350997Sbostic 		return (ERROR);
50450997Sbostic 	}
50550997Sbostic 	return (hash_access(HASH_DELETE, (DBT *)key, NULL));
50646366Sbostic }
50746366Sbostic 
50846366Sbostic /*
50950997Sbostic  * Assume that hashp has been set in wrapper routine.
51050997Sbostic  */
51146366Sbostic static int
51246366Sbostic hash_access(action, key, val)
51350997Sbostic 	ACTION action;
51450997Sbostic 	DBT *key, *val;
51546366Sbostic {
51650997Sbostic 	register BUFHEAD *rbufp;
51750997Sbostic 	BUFHEAD *bufp, *save_bufp;
51850997Sbostic 	register u_short *bp;
51950997Sbostic 	register int n, ndx, off, size;
52050997Sbostic 	register char *kp;
52150997Sbostic 	u_short pageno;
52246366Sbostic 
52346366Sbostic #ifdef HASH_STATISTICS
52446366Sbostic 	hash_accesses++;
52546366Sbostic #endif
52646366Sbostic 
52750997Sbostic 	off = hashp->BSIZE;
52846366Sbostic 	size = key->size;
52946950Sbostic 	kp = (char *)key->data;
53050997Sbostic 	rbufp = __get_buf(__call_hash(kp, size), NULL, 0);
53150997Sbostic 	if (!rbufp)
53250997Sbostic 		return (ERROR);
53346457Sbostic 	save_bufp = rbufp;
53446366Sbostic 
53546457Sbostic 	/* Pin the bucket chain */
53646457Sbostic 	rbufp->flags |= BUF_PIN;
53750997Sbostic 	for (bp = (u_short *)rbufp->page, n = *bp++, ndx = 1; ndx < n;)
53850997Sbostic 		if (bp[1] >= REAL_KEY) {
53950997Sbostic 			/* Real key/data pair */
54050997Sbostic 			if (size == off - *bp &&
54150997Sbostic 			    bcmp(kp, rbufp->page + *bp, size) == 0)
54250997Sbostic 				goto found;
54350997Sbostic 			off = bp[1];
54446366Sbostic #ifdef HASH_STATISTICS
54550997Sbostic 			hash_collisions++;
54646366Sbostic #endif
54750997Sbostic 			bp += 2;
54850997Sbostic 			ndx += 2;
54950997Sbostic 		} else if (bp[1] == OVFLPAGE) {
55050997Sbostic 			rbufp = __get_buf(*bp, rbufp, 0);
55150997Sbostic 			if (!rbufp) {
55250997Sbostic 				save_bufp->flags &= ~BUF_PIN;
55350997Sbostic 				return (ERROR);
55450997Sbostic 			}
55550997Sbostic 			/* FOR LOOP INIT */
55650997Sbostic 			bp = (u_short *)rbufp->page;
55750997Sbostic 			n = *bp++;
55850997Sbostic 			ndx = 1;
55950997Sbostic 			off = hashp->BSIZE;
56050997Sbostic 		} else if (bp[1] < REAL_KEY) {
56150997Sbostic 			if ((ndx = __find_bigpair(rbufp, ndx, kp, size)) > 0)
56250997Sbostic 				goto found;
56350997Sbostic 			if (ndx == -2) {
56450997Sbostic 				bufp = rbufp;
56550997Sbostic 				if (!(pageno = __find_last_page(&bufp))) {
56650997Sbostic 					ndx = 0;
56750997Sbostic 					rbufp = bufp;
56850997Sbostic 					break;	/* FOR */
56950997Sbostic 				}
57050997Sbostic 				rbufp = __get_buf(pageno, bufp, 0);
57150997Sbostic 				if (!rbufp) {
57250997Sbostic 					save_bufp->flags &= ~BUF_PIN;
57350997Sbostic 					return (ERROR);
57450997Sbostic 				}
57550997Sbostic 				/* FOR LOOP INIT */
57650997Sbostic 				bp = (u_short *)rbufp->page;
57750997Sbostic 				n = *bp++;
57850997Sbostic 				ndx = 1;
57950997Sbostic 				off = hashp->BSIZE;
58050997Sbostic 			} else {
58150997Sbostic 				save_bufp->flags &= ~BUF_PIN;
58250997Sbostic 				return (ERROR);
58350997Sbostic 			}
58446457Sbostic 		}
58546366Sbostic 
58646366Sbostic 	/* Not found */
58750997Sbostic 	switch (action) {
58850997Sbostic 	case HASH_PUT:
58950997Sbostic 	case HASH_PUTNEW:
59046366Sbostic 		if (__addel(rbufp, key, val)) {
59150997Sbostic 			save_bufp->flags &= ~BUF_PIN;
59250997Sbostic 			return (ERROR);
59346366Sbostic 		} else {
59450997Sbostic 			save_bufp->flags &= ~BUF_PIN;
59550997Sbostic 			return (SUCCESS);
59646366Sbostic 		}
59750997Sbostic 	case HASH_GET:
59850997Sbostic 	case HASH_DELETE:
59950997Sbostic 	default:
60046457Sbostic 		save_bufp->flags &= ~BUF_PIN;
60150997Sbostic 		return (ABNORMAL);
60246366Sbostic 	}
60346366Sbostic 
60446366Sbostic found:
60546366Sbostic 	switch (action) {
60650997Sbostic 	case HASH_PUTNEW:
60746457Sbostic 		save_bufp->flags &= ~BUF_PIN;
60850997Sbostic 		return (ABNORMAL);
60950997Sbostic 	case HASH_GET:
61046366Sbostic 		bp = (u_short *)rbufp->page;
611*51072Sbostic 		if (bp[ndx + 1] < REAL_KEY) {
61251057Sbostic 			if (__big_return(rbufp, ndx, val, 0))
61351057Sbostic 				return (ERROR);
614*51072Sbostic 		} else {
61550997Sbostic 			val->data = (u_char *)rbufp->page + (int)bp[ndx + 1];
61650997Sbostic 			val->size = bp[ndx] - bp[ndx + 1];
61746366Sbostic 		}
61846366Sbostic 		break;
61950997Sbostic 	case HASH_PUT:
62050997Sbostic 		if ((__delpair(rbufp, ndx)) || (__addel(rbufp, key, val))) {
62150997Sbostic 			save_bufp->flags &= ~BUF_PIN;
62250997Sbostic 			return (ERROR);
62346457Sbostic 		}
62446366Sbostic 		break;
62550997Sbostic 	case HASH_DELETE:
62650997Sbostic 		if (__delpair(rbufp, ndx))
62750997Sbostic 			return (ERROR);
62846366Sbostic 		break;
62946366Sbostic 	}
63046457Sbostic 	save_bufp->flags &= ~BUF_PIN;
63146366Sbostic 	return (SUCCESS);
63246366Sbostic }
63346366Sbostic 
63446366Sbostic static int
63546366Sbostic hash_seq(dbp, key, data, flag)
63650997Sbostic 	const DB *dbp;
63750997Sbostic 	DBT *key, *data;
63850997Sbostic 	u_int flag;
63946366Sbostic {
64050997Sbostic 	register u_int bucket;
64150997Sbostic 	register BUFHEAD *bufp;
64250997Sbostic 	u_short *bp, ndx;
64346366Sbostic 
64450997Sbostic 	if (flag && flag != R_FIRST && flag != R_NEXT) {
64550997Sbostic 		hashp->errno = errno = EINVAL;
64650997Sbostic 		return (ERROR);
64746366Sbostic 	}
64846366Sbostic 	hashp = (HTAB *)dbp->internal;
64950997Sbostic 	if (hashp->flags & O_WRONLY) {
65050997Sbostic 		hashp->errno = errno = EPERM;
65150997Sbostic 		return (ERROR);
65246366Sbostic 	}
65346366Sbostic #ifdef HASH_STATISTICS
65446366Sbostic 	hash_accesses++;
65546366Sbostic #endif
65650997Sbostic 	if ((hashp->cbucket < 0) || (flag == R_FIRST)) {
65750997Sbostic 		hashp->cbucket = 0;
65850997Sbostic 		hashp->cndx = 1;
65950997Sbostic 		hashp->cpage = NULL;
66046366Sbostic 	}
66150997Sbostic 	if (!(bufp = hashp->cpage)) {
66250997Sbostic 		for (bucket = hashp->cbucket; bucket <= hashp->MAX_BUCKET;
66350997Sbostic 		    bucket++, hashp->cndx = 1) {
66446366Sbostic 
66550997Sbostic 			bufp = __get_buf(bucket, NULL, 0);
66650997Sbostic 			if (!bufp)
66750997Sbostic 				return (ERROR);
66850997Sbostic 			hashp->cpage = bufp;
66950997Sbostic 			bp = (u_short *)bufp->page;
67050997Sbostic 			if (bp[0])
67150997Sbostic 				break;
67250997Sbostic 		}
67350997Sbostic 		hashp->cbucket = bucket;
67450997Sbostic 		if (hashp->cbucket > hashp->MAX_BUCKET) {
67550997Sbostic 			hashp->cbucket = -1;
67650997Sbostic 			return (ABNORMAL);
67750997Sbostic 		}
67850997Sbostic 	} else
67950997Sbostic 		bp = (u_short *)hashp->cpage->page;
68046366Sbostic 
68146366Sbostic #ifdef DEBUG
68250997Sbostic 	assert(bp);
68350997Sbostic 	assert(bufp);
68446366Sbostic #endif
68550997Sbostic 	while (bp[hashp->cndx + 1] == OVFLPAGE) {
68650997Sbostic 		bufp = hashp->cpage = __get_buf(bp[hashp->cndx], bufp, 0);
68750997Sbostic 		if (!bufp)
68850997Sbostic 			return (ERROR);
68950997Sbostic 		bp = (u_short *)(bufp->page);
69050997Sbostic 		hashp->cndx = 1;
69146366Sbostic 	}
69246366Sbostic 	ndx = hashp->cndx;
69350997Sbostic 	if (bp[ndx + 1] < REAL_KEY) {
69451057Sbostic 		if (__big_keydata(bufp, key, data, 1))
69550997Sbostic 			return (ERROR);
69646366Sbostic 	} else {
69750997Sbostic 		key->data = (u_char *)hashp->cpage->page + bp[ndx];
69850997Sbostic 		key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx];
69950997Sbostic 		data->data = (u_char *)hashp->cpage->page + bp[ndx + 1];
70050997Sbostic 		data->size = bp[ndx] - bp[ndx + 1];
70150997Sbostic 		ndx += 2;
70250997Sbostic 		if (ndx > bp[0]) {
70350997Sbostic 			hashp->cpage = NULL;
70450997Sbostic 			hashp->cbucket++;
70550997Sbostic 			hashp->cndx = 1;
70650997Sbostic 		} else
70750997Sbostic 			hashp->cndx = ndx;
70846366Sbostic 	}
70946366Sbostic 	return (SUCCESS);
71046366Sbostic }
71146366Sbostic 
71246366Sbostic /********************************* UTILITIES ************************/
71350997Sbostic 
71446366Sbostic /*
71550997Sbostic  * Returns:
71650997Sbostic  *	 0 ==> OK
71750997Sbostic  *	-1 ==> Error
71850997Sbostic  */
71946366Sbostic extern int
72046366Sbostic __expand_table()
72146366Sbostic {
72250997Sbostic 	u_int old_bucket, new_bucket;
72350997Sbostic 	int dirsize, new_segnum, spare_ndx;
72450997Sbostic 
72546366Sbostic #ifdef HASH_STATISTICS
72646366Sbostic 	hash_expansions++;
72746366Sbostic #endif
72846366Sbostic 	new_bucket = ++hashp->MAX_BUCKET;
72946366Sbostic 	old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK);
73046366Sbostic 
73146366Sbostic 	new_segnum = new_bucket >> hashp->SSHIFT;
73246366Sbostic 
73346366Sbostic 	/* Check if we need a new segment */
73450997Sbostic 	if (new_segnum >= hashp->nsegs) {
73550997Sbostic 		/* Check if we need to expand directory */
73650997Sbostic 		if (new_segnum >= hashp->DSIZE) {
73750997Sbostic 			/* Reallocate directory */
73850997Sbostic 			dirsize = hashp->DSIZE * sizeof(SEGMENT *);
73950997Sbostic 			if (!hash_realloc(&hashp->dir, dirsize, dirsize << 1))
74050997Sbostic 				return (-1);
74150997Sbostic 			hashp->DSIZE = dirsize << 1;
74246366Sbostic 		}
74350997Sbostic 		if (!(hashp->dir[new_segnum] =
74450997Sbostic 			calloc(hashp->SGSIZE, sizeof(SEGMENT))))
74550997Sbostic 			return (-1);
74650997Sbostic 		hashp->exsegs++;
74750997Sbostic 		hashp->nsegs++;
74846366Sbostic 	}
74946366Sbostic 	/*
75050997Sbostic 	 * If the split point is increasing (MAX_BUCKET's log base 2
75150997Sbostic 	 * * increases), we need to copy the current contents of the spare
75250997Sbostic 	 * split bucket to the next bucket.
75350997Sbostic 	 */
75446366Sbostic 	spare_ndx = __log2(hashp->MAX_BUCKET);
75551061Sbostic 	if ( spare_ndx > hashp->OVFL_POINT ) {
75651061Sbostic 		hashp->SPARES[spare_ndx] = hashp->SPARES[hashp->OVFL_POINT];
75751061Sbostic 		hashp->OVFL_POINT = spare_ndx;
75851061Sbostic 	}
75951061Sbostic 
76050997Sbostic 	if (new_bucket > hashp->HIGH_MASK) {
76150997Sbostic 		/* Starting a new doubling */
76250997Sbostic 		hashp->LOW_MASK = hashp->HIGH_MASK;
76350997Sbostic 		hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK;
76446366Sbostic 	}
76550997Sbostic 	/* Relocate records to the new bucket */
76650997Sbostic 	return (__split_page(old_bucket, new_bucket));
76750997Sbostic }
76846366Sbostic 
76946366Sbostic /*
77050997Sbostic  * If realloc guarantees that the pointer is not destroyed if the realloc
77150997Sbostic  * fails, then this routine can go away.
77250997Sbostic  */
77350997Sbostic static void *
77450997Sbostic hash_realloc(p_ptr, oldsize, newsize)
77550997Sbostic 	SEGMENT **p_ptr;
77650997Sbostic 	int oldsize, newsize;
77746366Sbostic {
77850997Sbostic 	register void *p;
77946366Sbostic 
78050997Sbostic 	if (p = malloc(newsize)) {
78150997Sbostic 		bcopy(*p_ptr, p, oldsize);
78250997Sbostic 		bzero(*p_ptr + oldsize, newsize - oldsize);
78346366Sbostic 		free(*p_ptr);
78446366Sbostic 		*p_ptr = p;
78546366Sbostic 	}
78646366Sbostic 	return (p);
78746366Sbostic }
78846366Sbostic 
78947251Sbostic extern u_int
79050997Sbostic __call_hash(k, len)
79150997Sbostic 	char *k;
79250997Sbostic 	int len;
79346366Sbostic {
79450997Sbostic 	int n, bucket;
79550997Sbostic 
79646366Sbostic 	n = hashp->hash(k, len);
79746366Sbostic 	bucket = n & hashp->HIGH_MASK;
79850997Sbostic 	if (bucket > hashp->MAX_BUCKET)
79950997Sbostic 		bucket = bucket & hashp->LOW_MASK;
80050997Sbostic 	return (bucket);
80146366Sbostic }
80246366Sbostic 
80346366Sbostic /*
80450997Sbostic  * Allocate segment table.  On error, destroy the table and set errno.
80550997Sbostic  *
80650997Sbostic  * Returns 0 on success
80750997Sbostic  */
80846366Sbostic static int
80950997Sbostic alloc_segs(nsegs)
81050997Sbostic 	int nsegs;
81146366Sbostic {
81250997Sbostic 	register int i;
81350997Sbostic 	register SEGMENT store;
81446366Sbostic 
81550997Sbostic 	int save_errno;
81646366Sbostic 
81750997Sbostic 	if (!(hashp->dir = calloc(hashp->DSIZE, sizeof(SEGMENT *)))) {
81850997Sbostic 		save_errno = errno;
81950997Sbostic 		(void)hdestroy();
82050997Sbostic 		errno = save_errno;
82150997Sbostic 		return (-1);
82250997Sbostic 	}
82350997Sbostic 	/* Allocate segments */
82450997Sbostic 	store = calloc(nsegs << hashp->SSHIFT, sizeof(SEGMENT));
82550997Sbostic 	if (!store) {
82650997Sbostic 		save_errno = errno;
82750997Sbostic 		(void)hdestroy();
82850997Sbostic 		errno = save_errno;
82950997Sbostic 		return (-1);
83050997Sbostic 	}
83150997Sbostic 	for (i = 0; i < nsegs; i++, hashp->nsegs++)
83250997Sbostic 		hashp->dir[i] = &store[i << hashp->SSHIFT];
83350997Sbostic 	return (0);
83446366Sbostic }
83546366Sbostic 
83650997Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
83746366Sbostic /*
83850997Sbostic  * Hashp->hdr needs to be byteswapped.
83950997Sbostic  */
84046366Sbostic static void
84150997Sbostic swap_header_copy(srcp, destp)
84250997Sbostic 	HASHHDR *srcp, *destp;
84346366Sbostic {
84450997Sbostic 	int i;
84546366Sbostic 
84650997Sbostic 	BLSWAP_COPY(srcp->magic, destp->magic);
84750997Sbostic 	BLSWAP_COPY(srcp->version, destp->version);
84850997Sbostic 	BLSWAP_COPY(srcp->lorder, destp->lorder);
84950997Sbostic 	BLSWAP_COPY(srcp->bsize, destp->bsize);
85050997Sbostic 	BLSWAP_COPY(srcp->bshift, destp->bshift);
85150997Sbostic 	BLSWAP_COPY(srcp->dsize, destp->dsize);
85250997Sbostic 	BLSWAP_COPY(srcp->ssize, destp->ssize);
85350997Sbostic 	BLSWAP_COPY(srcp->sshift, destp->sshift);
85451061Sbostic 	BLSWAP_COPY(srcp->ovfl_point, destp->ovfl_point);
85551061Sbostic 	BLSWAP_COPY(srcp->last_freed, destp->last_freed);
85650997Sbostic 	BLSWAP_COPY(srcp->max_bucket, destp->max_bucket);
85750997Sbostic 	BLSWAP_COPY(srcp->high_mask, destp->high_mask);
85850997Sbostic 	BLSWAP_COPY(srcp->low_mask, destp->low_mask);
85950997Sbostic 	BLSWAP_COPY(srcp->ffactor, destp->ffactor);
86050997Sbostic 	BLSWAP_COPY(srcp->nkeys, destp->nkeys);
86150997Sbostic 	BLSWAP_COPY(srcp->hdrpages, destp->hdrpages);
86250997Sbostic 	BLSWAP_COPY(srcp->h_charkey, destp->h_charkey);
86350997Sbostic 	for (i = 0; i < NCACHED; i++) {
86450997Sbostic 		BLSWAP_COPY(srcp->spares[i], destp->spares[i]);
86550997Sbostic 		BSSWAP_COPY(srcp->bitmaps[i], destp->bitmaps[i]);
86650997Sbostic 	}
86746366Sbostic }
86846366Sbostic 
86946366Sbostic static void
87050997Sbostic swap_header()
87146366Sbostic {
87250997Sbostic 	HASHHDR *hdrp;
87350997Sbostic 	int i;
87446366Sbostic 
87550997Sbostic 	hdrp = &hashp->hdr;
87646366Sbostic 
87750997Sbostic 	BLSWAP(hdrp->magic);
87850997Sbostic 	BLSWAP(hdrp->version);
87950997Sbostic 	BLSWAP(hdrp->lorder);
88050997Sbostic 	BLSWAP(hdrp->bsize);
88150997Sbostic 	BLSWAP(hdrp->bshift);
88250997Sbostic 	BLSWAP(hdrp->dsize);
88350997Sbostic 	BLSWAP(hdrp->ssize);
88450997Sbostic 	BLSWAP(hdrp->sshift);
88551061Sbostic 	BLSWAP(hdrp->ovfl_point);
88651061Sbostic 	BLSWAP(hdrp->last_freed);
88750997Sbostic 	BLSWAP(hdrp->max_bucket);
88850997Sbostic 	BLSWAP(hdrp->high_mask);
88950997Sbostic 	BLSWAP(hdrp->low_mask);
89050997Sbostic 	BLSWAP(hdrp->ffactor);
89150997Sbostic 	BLSWAP(hdrp->nkeys);
89250997Sbostic 	BLSWAP(hdrp->hdrpages);
89350997Sbostic 	BLSWAP(hdrp->h_charkey);
89450997Sbostic 	for (i = 0; i < NCACHED; i++) {
89550997Sbostic 		BLSWAP(hdrp->spares[i]);
89650997Sbostic 		BSSWAP(hdrp->bitmaps[i]);
89750997Sbostic 	}
89846366Sbostic }
89950997Sbostic #endif
900