xref: /csrg-svn/lib/libc/db/hash/hash.c (revision 51057)
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*51057Sbostic static char sccsid[] = "@(#)hash.c	5.16 (Berkeley) 09/08/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));
36*51057Sbostic 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 *
7050997Sbostic 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);
12650997Sbostic 		if (hashp->VERSION != VERSION_NO)
12750997Sbostic 			RETURN_ERROR(EFTYPE, error1);
12850997Sbostic 		if (hashp->hash(CHARKEY, sizeof(CHARKEY)) != hashp->H_CHARKEY)
12950997Sbostic 			RETURN_ERROR(EFTYPE, error1);
130*51057Sbostic 		/*
131*51057Sbostic 		 * Figure out how many segments we need.  Max_Bucket is the
132*51057Sbostic 		 * maximum bucket number, so the number of buckets is
133*51057Sbostic 		 * max_bucket + 1.
134*51057Sbostic 		 */
135*51057Sbostic 		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 */
14550997Sbostic 		bpages = (hashp->SPARES[__log2(hashp->MAX_BUCKET)] +
14650997Sbostic 		    (hashp->BSIZE << BYTE_SHIFT) - 1) >>
14750997Sbostic 		    (hashp->BSHIFT + BYTE_SHIFT);
14846366Sbostic 
14950997Sbostic 		hashp->nmaps = bpages;
150*51057Sbostic 		(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,
17950997Sbostic "%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",
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,
18950997Sbostic 	    "HIGH MASK       ", hashp->HIGH_MASK,
19050997Sbostic 	    "LOW  MASK       ", hashp->LOW_MASK,
19150997Sbostic 	    "NSEGS           ", hashp->nsegs,
19250997Sbostic 	    "NKEYS           ", hashp->NKEYS);
19346366Sbostic #endif
19446366Sbostic #ifdef HASH_STATISTICS
19546366Sbostic 	hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0;
19646366Sbostic #endif
19750997Sbostic 	return (dbp);
19846366Sbostic 
19946366Sbostic error1:
20050997Sbostic 	(void)close(hashp->fp);
20146366Sbostic 
20246366Sbostic error0:
20350997Sbostic 	free(hashp);
20450997Sbostic 	errno = save_errno;
20550997Sbostic 	return (NULL);
20646366Sbostic }
20746366Sbostic 
20846366Sbostic static int
20950997Sbostic hash_close(dbp)
21050997Sbostic 	DB *dbp;
21146366Sbostic {
21250997Sbostic 	int retval;
21346457Sbostic 
21450997Sbostic 	if (!dbp)
21550997Sbostic 		return (ERROR);
21646366Sbostic 	hashp = (HTAB *)dbp->internal;
21746457Sbostic 	retval = hdestroy();
21850997Sbostic 	free(dbp);
21950997Sbostic 	return (retval);
22046366Sbostic }
22146366Sbostic 
22246366Sbostic /************************** LOCAL CREATION ROUTINES **********************/
22350997Sbostic static HTAB *
22450997Sbostic init_hash(info)
22550997Sbostic 	HASHINFO *info;
22646366Sbostic {
22750997Sbostic 	int nelem;
22846366Sbostic 
22946366Sbostic 	nelem = 1;
23050997Sbostic 	hashp->LORDER = BYTE_ORDER;
23150997Sbostic 	hashp->BSIZE = DEF_BUCKET_SIZE;
23250997Sbostic 	hashp->BSHIFT = DEF_BUCKET_SHIFT;
23350997Sbostic 	hashp->SGSIZE = DEF_SEGSIZE;
23450997Sbostic 	hashp->SSHIFT = DEF_SEGSIZE_SHIFT;
23550997Sbostic 	hashp->DSIZE = DEF_DIRSIZE;
23650997Sbostic 	hashp->FFACTOR = DEF_FFACTOR;
23750997Sbostic 	hashp->hash = default_hash;
23850997Sbostic 	bzero(hashp->SPARES, sizeof(hashp->SPARES));
23946366Sbostic 
24050997Sbostic 	if (info) {
24150997Sbostic 		if (info->bsize) {
24250997Sbostic 			/* Round pagesize up to power of 2 */
24350997Sbostic 			hashp->BSHIFT = __log2(info->bsize);
24450997Sbostic 			hashp->BSIZE = 1 << hashp->BSHIFT;
24550997Sbostic 			if (hashp->BSIZE > MAX_BSIZE) {
24650997Sbostic 				errno = EINVAL;
24750997Sbostic 				return (NULL);
24850997Sbostic 			}
24946366Sbostic 		}
25050997Sbostic 		if (info->ffactor)
25150997Sbostic 			hashp->FFACTOR = info->ffactor;
25250997Sbostic 		if (info->hash)
25350997Sbostic 			hashp->hash = info->hash;
25450997Sbostic 		if (info->nelem)
25550997Sbostic 			nelem = info->nelem;
25650997Sbostic 		if (info->lorder) {
25750997Sbostic 			if (info->lorder != BIG_ENDIAN &&
25850997Sbostic 			    info->lorder != LITTLE_ENDIAN) {
25950997Sbostic 				errno = EINVAL;
26050997Sbostic 				return (NULL);
26150997Sbostic 			}
26250997Sbostic 			hashp->LORDER = info->lorder;
26346366Sbostic 		}
26446366Sbostic 	}
26546366Sbostic 	/* init_htab should destroy the table and set errno if it fails */
26650997Sbostic 	if (init_htab(nelem))
26750997Sbostic 		return (NULL);
26850997Sbostic 	else
26950997Sbostic 		return (hashp);
27046366Sbostic }
27146366Sbostic /*
27250997Sbostic  * This calls alloc_segs which may run out of memory.  Alloc_segs will destroy
27350997Sbostic  * the table and set errno, so we just pass the error information along.
27450997Sbostic  *
27550997Sbostic  * Returns 0 on No Error
27650997Sbostic  */
27746366Sbostic static int
27850997Sbostic init_htab(nelem)
27950997Sbostic 	int nelem;
28046366Sbostic {
28150997Sbostic 	register int nbuckets, nsegs;
28250997Sbostic 	int l2;
28346366Sbostic 
28450997Sbostic 	/*
28550997Sbostic 	 * Divide number of elements by the fill factor and determine a
28650997Sbostic 	 * desired number of buckets.  Allocate space for the next greater
28750997Sbostic 	 * power of two number of buckets.
28850997Sbostic 	 */
28946366Sbostic 	nelem = (nelem - 1) / hashp->FFACTOR + 1;
29046366Sbostic 
29146366Sbostic 	l2 = __log2(nelem);
29246366Sbostic 	nbuckets = 1 << l2;
29350997Sbostic 	nbuckets = MAX(nbuckets, 2);
29446366Sbostic 
29546366Sbostic 	hashp->SPARES[l2] = l2 + 1;
29650997Sbostic 	hashp->SPARES[l2 + 1] = l2 + 1;
29750997Sbostic 	/* First bitmap page is at: splitpoint l2 page offset 1 */
298*51057Sbostic 	if (__init_bitmap(OADDR_OF(l2, 1), l2 + 1, 0))
299*51057Sbostic 		return (-1);
30046366Sbostic 
30146366Sbostic 	hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1;
30246366Sbostic 	hashp->HIGH_MASK = (nbuckets << 1) - 1;
30350997Sbostic 	hashp->HDRPAGES = ((MAX(sizeof(HASHHDR), MINHDRSIZE) - 1) >>
30450997Sbostic 	    hashp->BSHIFT) + 1;
30546366Sbostic 
30646366Sbostic 	nsegs = (nbuckets - 1) / hashp->SGSIZE + 1;
30746366Sbostic 	nsegs = 1 << __log2(nsegs);
30846366Sbostic 
30950997Sbostic 	if (nsegs > hashp->DSIZE)
31050997Sbostic 		hashp->DSIZE = nsegs;
31150997Sbostic 	return (alloc_segs(nsegs));
31246366Sbostic }
31346366Sbostic 
31446366Sbostic /********************** DESTROY/CLOSE ROUTINES ************************/
31546366Sbostic 
31646366Sbostic /*
31750997Sbostic  * Flushes any changes to the file if necessary and destroys the hashp
31850997Sbostic  * structure, freeing all allocated space.
31950997Sbostic  */
32046366Sbostic static int
32146366Sbostic hdestroy()
32246366Sbostic {
32350997Sbostic 	int i, save_errno;
32446366Sbostic 
32546366Sbostic 	save_errno = 0;
32646366Sbostic 
32746366Sbostic 	if (hashp != NULL) {
32846366Sbostic #ifdef HASH_STATISTICS
32950997Sbostic 		(void)fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n",
33050997Sbostic 		    hash_accesses, hash_collisions);
33150997Sbostic 		(void)fprintf(stderr, "hdestroy: expansions %ld\n",
33250997Sbostic 		    hash_expansions);
33350997Sbostic 		(void)fprintf(stderr, "hdestroy: overflows %ld\n",
33450997Sbostic 		    hash_overflows);
33550997Sbostic 		(void)fprintf(stderr, "keys %ld maxp %d segmentcount %d\n",
33650997Sbostic 		    hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs);
33746366Sbostic 
33850997Sbostic 		for (i = 0; i < NCACHED; i++)
33950997Sbostic 			(void)fprintf(stderr,
34050997Sbostic 			    "spares[%d] = %d\n", i, hashp->SPARES[i]);
34146366Sbostic #endif
34250997Sbostic 		/*
34350997Sbostic 		 * Call on buffer manager to free buffers, and if required,
34450997Sbostic 		 * write them to disk.
34550997Sbostic 		 */
34650997Sbostic 		if (__buf_free(1, hashp->save_file))
34750997Sbostic 			save_errno = errno;
34850997Sbostic 		if (hashp->dir) {
34950997Sbostic 			free(*hashp->dir);	/* Free initial segments */
35050997Sbostic 			/* Free extra segments */
35150997Sbostic 			while (hashp->exsegs--)
35250997Sbostic 				free(hashp->dir[--hashp->nsegs]);
35350997Sbostic 			free(hashp->dir);
35446366Sbostic 		}
35550997Sbostic 		if (flush_meta() && !save_errno)
35650997Sbostic 			save_errno = errno;
35747251Sbostic 		/* Free Bigmaps */
35850997Sbostic 		for (i = 0; i < hashp->nmaps; i++)
35950997Sbostic 			if (hashp->mapp[i])
36050997Sbostic 				free(hashp->mapp[i]);
36146503Sbostic 
36250997Sbostic 		if (hashp->fp != -1)
36350997Sbostic 			(void)close(hashp->fp);
36450997Sbostic 		free(hashp);
36546366Sbostic 		hashp = NULL;
36646366Sbostic 	}
36750997Sbostic 	if (save_errno) {
36850997Sbostic 		errno = save_errno;
36950997Sbostic 		return (ERROR);
37046366Sbostic 	}
37150997Sbostic 	return (SUCCESS);
37246366Sbostic }
37350997Sbostic /*
37450997Sbostic  * Write modified pages to disk
37550997Sbostic  *
37650997Sbostic  * Returns:
37750997Sbostic  *	 0 == OK
37850997Sbostic  *	-1 ERROR
37950997Sbostic  */
38046366Sbostic static int
38146366Sbostic hash_sync(dbp)
38250997Sbostic 	const DB *dbp;
38346366Sbostic {
38450997Sbostic 	if (!dbp)
38550997Sbostic 		return (ERROR);
38646366Sbostic 	hashp = (HTAB *)dbp->internal;
38746366Sbostic 
38850997Sbostic 	if (!hashp->save_file)
38950997Sbostic 		return (0);
39050997Sbostic 	if (__buf_free(0, 1) || flush_meta())
39150997Sbostic 		return (ERROR);
39246366Sbostic 	hashp->new_file = 0;
39350997Sbostic 	return (0);
39446366Sbostic }
39546366Sbostic 
39646366Sbostic /*
39750997Sbostic  * Returns:
39850997Sbostic  *	 0 == OK
39950997Sbostic  *	-1 indicates that errno should be set
40050997Sbostic  */
40146366Sbostic static int
40246366Sbostic flush_meta()
40346366Sbostic {
40450997Sbostic 	HASHHDR *whdrp;
40550997Sbostic 	int fp, i, wsize;
40646366Sbostic 
40750997Sbostic 	if (!hashp->save_file)
40850997Sbostic 		return (0);
40946366Sbostic 	hashp->MAGIC = HASHMAGIC;
41046366Sbostic 	hashp->VERSION = VERSION_NO;
41150997Sbostic 	hashp->H_CHARKEY = hashp->hash(CHARKEY, sizeof(CHARKEY));
41246366Sbostic 
41346366Sbostic 	fp = hashp->fp;
41446366Sbostic 	whdrp = &hashp->hdr;
41546366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
41646366Sbostic 	whdrp = &whdr;
41750997Sbostic 	swap_header_copy(&hashp->hdr, whdrp);
41846366Sbostic #endif
41950997Sbostic 	if ((lseek(fp, 0, SEEK_SET) == -1) ||
42050997Sbostic 	    ((wsize = write(fp, whdrp, sizeof(HASHHDR))) == -1))
42150997Sbostic 		return (-1);
42250997Sbostic 	else
42350997Sbostic 		if (wsize != sizeof(HASHHDR)) {
42450997Sbostic 			errno = EFTYPE;
42550997Sbostic 			hashp->errno = errno;
42650997Sbostic 			return (-1);
42746366Sbostic 		}
42850997Sbostic 	for (i = 0; i < NCACHED; i++)
42950997Sbostic 		if (hashp->mapp[i])
43050997Sbostic 			if (!__put_page((char *)hashp->mapp[i],
43150997Sbostic 				hashp->BITMAPS[i], 0, 1))
43250997Sbostic 				return (-1);
43350997Sbostic 	return (0);
43446366Sbostic }
43550997Sbostic 
43646366Sbostic /*******************************SEARCH ROUTINES *****************************/
43746366Sbostic /*
43850997Sbostic  * All the access routines return
43950997Sbostic  *
44050997Sbostic  * Returns:
44150997Sbostic  *	 0 on SUCCESS
44250997Sbostic  *	 1 to indicate an external ERROR (i.e. key not found, etc)
44350997Sbostic  *	-1 to indicate an internal ERROR (i.e. out of memory, etc)
44450997Sbostic  */
44546366Sbostic static int
44650997Sbostic hash_get(dbp, key, data, flag)
44750997Sbostic 	const DB *dbp;
448*51057Sbostic 	const DBT *key;
449*51057Sbostic 	DBT *data;
45050997Sbostic 	u_int flag;
45146366Sbostic {
45250997Sbostic 	if (flag) {
45350997Sbostic 		hashp->errno = errno = EINVAL;
45450997Sbostic 		return (ERROR);
45550997Sbostic 	}
45650997Sbostic 	hashp = (HTAB *)dbp->internal;
45750997Sbostic 	if (hashp->flags & O_WRONLY) {
45850997Sbostic 		hashp->errno = errno = EPERM;
45950997Sbostic 		return (ERROR);
46050997Sbostic 	}
461*51057Sbostic 	return (hash_access(HASH_GET, (DBT *)key, data));
46246366Sbostic }
46346366Sbostic 
46446366Sbostic static int
46550997Sbostic hash_put(dbp, key, data, flag)
46650997Sbostic 	const DB *dbp;
46750997Sbostic 	const DBT *key, *data;
46850997Sbostic 	u_int flag;
46946366Sbostic {
47050997Sbostic 	if (flag && flag != R_NOOVERWRITE) {
47150997Sbostic 		hashp->errno = errno = EINVAL;
47250997Sbostic 		return (ERROR);
47350997Sbostic 	}
47450997Sbostic 	hashp = (HTAB *)dbp->internal;
47550997Sbostic 	if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
47650997Sbostic 		hashp->errno = errno = EPERM;
47750997Sbostic 		return (ERROR);
47850997Sbostic 	}
479*51057Sbostic 	return (hash_access(flag == R_NOOVERWRITE ?
48050997Sbostic 	    HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data));
48146366Sbostic }
48246366Sbostic 
48346366Sbostic static int
48450997Sbostic hash_delete(dbp, key, flag)
48550997Sbostic 	const DB *dbp;
48650997Sbostic 	const DBT *key;
48750997Sbostic 	u_int flag;		/* Ignored */
48846366Sbostic {
48950997Sbostic 	if (flag && flag != R_CURSOR) {
49050997Sbostic 		hashp->errno = errno = EINVAL;
49150997Sbostic 		return (ERROR);
49250997Sbostic 	}
49350997Sbostic 	hashp = (HTAB *)dbp->internal;
49450997Sbostic 	if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
49550997Sbostic 		hashp->errno = errno = EPERM;
49650997Sbostic 		return (ERROR);
49750997Sbostic 	}
49850997Sbostic 	return (hash_access(HASH_DELETE, (DBT *)key, NULL));
49946366Sbostic }
50046366Sbostic 
50146366Sbostic /*
50250997Sbostic  * Assume that hashp has been set in wrapper routine.
50350997Sbostic  */
50446366Sbostic static int
50546366Sbostic hash_access(action, key, val)
50650997Sbostic 	ACTION action;
50750997Sbostic 	DBT *key, *val;
50846366Sbostic {
50950997Sbostic 	register BUFHEAD *rbufp;
51050997Sbostic 	BUFHEAD *bufp, *save_bufp;
51150997Sbostic 	register u_short *bp;
51250997Sbostic 	register int n, ndx, off, size;
51350997Sbostic 	register char *kp;
51450997Sbostic 	u_short pageno;
51546366Sbostic 
51646366Sbostic #ifdef HASH_STATISTICS
51746366Sbostic 	hash_accesses++;
51846366Sbostic #endif
51946366Sbostic 
52050997Sbostic 	off = hashp->BSIZE;
52146366Sbostic 	size = key->size;
52246950Sbostic 	kp = (char *)key->data;
52350997Sbostic 	rbufp = __get_buf(__call_hash(kp, size), NULL, 0);
52450997Sbostic 	if (!rbufp)
52550997Sbostic 		return (ERROR);
52646457Sbostic 	save_bufp = rbufp;
52746366Sbostic 
52846457Sbostic 	/* Pin the bucket chain */
52946457Sbostic 	rbufp->flags |= BUF_PIN;
53050997Sbostic 	for (bp = (u_short *)rbufp->page, n = *bp++, ndx = 1; ndx < n;)
53150997Sbostic 		if (bp[1] >= REAL_KEY) {
53250997Sbostic 			/* Real key/data pair */
53350997Sbostic 			if (size == off - *bp &&
53450997Sbostic 			    bcmp(kp, rbufp->page + *bp, size) == 0)
53550997Sbostic 				goto found;
53650997Sbostic 			off = bp[1];
53746366Sbostic #ifdef HASH_STATISTICS
53850997Sbostic 			hash_collisions++;
53946366Sbostic #endif
54050997Sbostic 			bp += 2;
54150997Sbostic 			ndx += 2;
54250997Sbostic 		} else if (bp[1] == OVFLPAGE) {
54350997Sbostic 			rbufp = __get_buf(*bp, rbufp, 0);
54450997Sbostic 			if (!rbufp) {
54550997Sbostic 				save_bufp->flags &= ~BUF_PIN;
54650997Sbostic 				return (ERROR);
54750997Sbostic 			}
54850997Sbostic 			/* FOR LOOP INIT */
54950997Sbostic 			bp = (u_short *)rbufp->page;
55050997Sbostic 			n = *bp++;
55150997Sbostic 			ndx = 1;
55250997Sbostic 			off = hashp->BSIZE;
55350997Sbostic 		} else if (bp[1] < REAL_KEY) {
55450997Sbostic 			if ((ndx = __find_bigpair(rbufp, ndx, kp, size)) > 0)
55550997Sbostic 				goto found;
55650997Sbostic 			if (ndx == -2) {
55750997Sbostic 				bufp = rbufp;
55850997Sbostic 				if (!(pageno = __find_last_page(&bufp))) {
55950997Sbostic 					ndx = 0;
56050997Sbostic 					rbufp = bufp;
56150997Sbostic 					break;	/* FOR */
56250997Sbostic 				}
56350997Sbostic 				rbufp = __get_buf(pageno, bufp, 0);
56450997Sbostic 				if (!rbufp) {
56550997Sbostic 					save_bufp->flags &= ~BUF_PIN;
56650997Sbostic 					return (ERROR);
56750997Sbostic 				}
56850997Sbostic 				/* FOR LOOP INIT */
56950997Sbostic 				bp = (u_short *)rbufp->page;
57050997Sbostic 				n = *bp++;
57150997Sbostic 				ndx = 1;
57250997Sbostic 				off = hashp->BSIZE;
57350997Sbostic 			} else {
57450997Sbostic 				save_bufp->flags &= ~BUF_PIN;
57550997Sbostic 				return (ERROR);
57650997Sbostic 			}
57746457Sbostic 		}
57846366Sbostic 
57946366Sbostic 	/* Not found */
58050997Sbostic 	switch (action) {
58150997Sbostic 	case HASH_PUT:
58250997Sbostic 	case HASH_PUTNEW:
58346366Sbostic 		if (__addel(rbufp, key, val)) {
58450997Sbostic 			save_bufp->flags &= ~BUF_PIN;
58550997Sbostic 			return (ERROR);
58646366Sbostic 		} else {
58750997Sbostic 			save_bufp->flags &= ~BUF_PIN;
58850997Sbostic 			return (SUCCESS);
58946366Sbostic 		}
59050997Sbostic 	case HASH_GET:
59150997Sbostic 	case HASH_DELETE:
59250997Sbostic 	default:
59346457Sbostic 		save_bufp->flags &= ~BUF_PIN;
59450997Sbostic 		return (ABNORMAL);
59546366Sbostic 	}
59646366Sbostic 
59746366Sbostic found:
59846366Sbostic 	switch (action) {
59950997Sbostic 	case HASH_PUTNEW:
60046457Sbostic 		save_bufp->flags &= ~BUF_PIN;
60150997Sbostic 		return (ABNORMAL);
60250997Sbostic 	case HASH_GET:
60346366Sbostic 		bp = (u_short *)rbufp->page;
60450997Sbostic 		if (bp[ndx + 1] < REAL_KEY)
605*51057Sbostic 			if (__big_return(rbufp, ndx, val, 0))
606*51057Sbostic 				return (ERROR);
60746366Sbostic 		else {
60850997Sbostic 			val->data = (u_char *)rbufp->page + (int)bp[ndx + 1];
60950997Sbostic 			val->size = bp[ndx] - bp[ndx + 1];
61046366Sbostic 		}
61146366Sbostic 		break;
61250997Sbostic 	case HASH_PUT:
61350997Sbostic 		if ((__delpair(rbufp, ndx)) || (__addel(rbufp, key, val))) {
61450997Sbostic 			save_bufp->flags &= ~BUF_PIN;
61550997Sbostic 			return (ERROR);
61646457Sbostic 		}
61746366Sbostic 		break;
61850997Sbostic 	case HASH_DELETE:
61950997Sbostic 		if (__delpair(rbufp, ndx))
62050997Sbostic 			return (ERROR);
62146366Sbostic 		break;
62246366Sbostic 	}
62346457Sbostic 	save_bufp->flags &= ~BUF_PIN;
62446366Sbostic 	return (SUCCESS);
62546366Sbostic }
62646366Sbostic 
62746366Sbostic static int
62846366Sbostic hash_seq(dbp, key, data, flag)
62950997Sbostic 	const DB *dbp;
63050997Sbostic 	DBT *key, *data;
63150997Sbostic 	u_int flag;
63246366Sbostic {
63350997Sbostic 	register u_int bucket;
63450997Sbostic 	register BUFHEAD *bufp;
63550997Sbostic 	u_short *bp, ndx;
63646366Sbostic 
63750997Sbostic 	if (flag && flag != R_FIRST && flag != R_NEXT) {
63850997Sbostic 		hashp->errno = errno = EINVAL;
63950997Sbostic 		return (ERROR);
64046366Sbostic 	}
64146366Sbostic 	hashp = (HTAB *)dbp->internal;
64250997Sbostic 	if (hashp->flags & O_WRONLY) {
64350997Sbostic 		hashp->errno = errno = EPERM;
64450997Sbostic 		return (ERROR);
64546366Sbostic 	}
64646366Sbostic #ifdef HASH_STATISTICS
64746366Sbostic 	hash_accesses++;
64846366Sbostic #endif
64950997Sbostic 	if ((hashp->cbucket < 0) || (flag == R_FIRST)) {
65050997Sbostic 		hashp->cbucket = 0;
65150997Sbostic 		hashp->cndx = 1;
65250997Sbostic 		hashp->cpage = NULL;
65346366Sbostic 	}
65450997Sbostic 	if (!(bufp = hashp->cpage)) {
65550997Sbostic 		for (bucket = hashp->cbucket; bucket <= hashp->MAX_BUCKET;
65650997Sbostic 		    bucket++, hashp->cndx = 1) {
65746366Sbostic 
65850997Sbostic 			bufp = __get_buf(bucket, NULL, 0);
65950997Sbostic 			if (!bufp)
66050997Sbostic 				return (ERROR);
66150997Sbostic 			hashp->cpage = bufp;
66250997Sbostic 			bp = (u_short *)bufp->page;
66350997Sbostic 			if (bp[0])
66450997Sbostic 				break;
66550997Sbostic 		}
66650997Sbostic 		hashp->cbucket = bucket;
66750997Sbostic 		if (hashp->cbucket > hashp->MAX_BUCKET) {
66850997Sbostic 			hashp->cbucket = -1;
66950997Sbostic 			return (ABNORMAL);
67050997Sbostic 		}
67150997Sbostic 	} else
67250997Sbostic 		bp = (u_short *)hashp->cpage->page;
67346366Sbostic 
67446366Sbostic #ifdef DEBUG
67550997Sbostic 	assert(bp);
67650997Sbostic 	assert(bufp);
67746366Sbostic #endif
67850997Sbostic 	while (bp[hashp->cndx + 1] == OVFLPAGE) {
67950997Sbostic 		bufp = hashp->cpage = __get_buf(bp[hashp->cndx], bufp, 0);
68050997Sbostic 		if (!bufp)
68150997Sbostic 			return (ERROR);
68250997Sbostic 		bp = (u_short *)(bufp->page);
68350997Sbostic 		hashp->cndx = 1;
68446366Sbostic 	}
68546366Sbostic 	ndx = hashp->cndx;
68650997Sbostic 	if (bp[ndx + 1] < REAL_KEY) {
687*51057Sbostic 		if (__big_keydata(bufp, key, data, 1))
68850997Sbostic 			return (ERROR);
68946366Sbostic 	} else {
69050997Sbostic 		key->data = (u_char *)hashp->cpage->page + bp[ndx];
69150997Sbostic 		key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx];
69250997Sbostic 		data->data = (u_char *)hashp->cpage->page + bp[ndx + 1];
69350997Sbostic 		data->size = bp[ndx] - bp[ndx + 1];
69450997Sbostic 		ndx += 2;
69550997Sbostic 		if (ndx > bp[0]) {
69650997Sbostic 			hashp->cpage = NULL;
69750997Sbostic 			hashp->cbucket++;
69850997Sbostic 			hashp->cndx = 1;
69950997Sbostic 		} else
70050997Sbostic 			hashp->cndx = ndx;
70146366Sbostic 	}
70246366Sbostic 	return (SUCCESS);
70346366Sbostic }
70446366Sbostic 
70546366Sbostic /********************************* UTILITIES ************************/
70650997Sbostic 
70746366Sbostic /*
70850997Sbostic  * Returns:
70950997Sbostic  *	 0 ==> OK
71050997Sbostic  *	-1 ==> Error
71150997Sbostic  */
71246366Sbostic extern int
71346366Sbostic __expand_table()
71446366Sbostic {
71550997Sbostic 	u_int old_bucket, new_bucket;
71650997Sbostic 	int dirsize, new_segnum, spare_ndx;
71750997Sbostic 
71846366Sbostic #ifdef HASH_STATISTICS
71946366Sbostic 	hash_expansions++;
72046366Sbostic #endif
72146366Sbostic 	new_bucket = ++hashp->MAX_BUCKET;
72246366Sbostic 	old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK);
72346366Sbostic 
72446366Sbostic 	new_segnum = new_bucket >> hashp->SSHIFT;
72546366Sbostic 
72646366Sbostic 	/* Check if we need a new segment */
72750997Sbostic 	if (new_segnum >= hashp->nsegs) {
72850997Sbostic 		/* Check if we need to expand directory */
72950997Sbostic 		if (new_segnum >= hashp->DSIZE) {
73050997Sbostic 			/* Reallocate directory */
73150997Sbostic 			dirsize = hashp->DSIZE * sizeof(SEGMENT *);
73250997Sbostic 			if (!hash_realloc(&hashp->dir, dirsize, dirsize << 1))
73350997Sbostic 				return (-1);
73450997Sbostic 			hashp->DSIZE = dirsize << 1;
73546366Sbostic 		}
73650997Sbostic 		if (!(hashp->dir[new_segnum] =
73750997Sbostic 			calloc(hashp->SGSIZE, sizeof(SEGMENT))))
73850997Sbostic 			return (-1);
73950997Sbostic 		hashp->exsegs++;
74050997Sbostic 		hashp->nsegs++;
74146366Sbostic 	}
74246366Sbostic 	/*
74350997Sbostic 	 * If the split point is increasing (MAX_BUCKET's log base 2
74450997Sbostic 	 * * increases), we need to copy the current contents of the spare
74550997Sbostic 	 * split bucket to the next bucket.
74650997Sbostic 	 */
74746366Sbostic 	spare_ndx = __log2(hashp->MAX_BUCKET);
74850997Sbostic 	if (spare_ndx != (__log2(hashp->MAX_BUCKET - 1)))
74950997Sbostic 		hashp->SPARES[spare_ndx] = hashp->SPARES[spare_ndx - 1];
75050997Sbostic 	if (new_bucket > hashp->HIGH_MASK) {
75150997Sbostic 		/* Starting a new doubling */
75250997Sbostic 		hashp->LOW_MASK = hashp->HIGH_MASK;
75350997Sbostic 		hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK;
75446366Sbostic 	}
75550997Sbostic 	/* Relocate records to the new bucket */
75650997Sbostic 	return (__split_page(old_bucket, new_bucket));
75750997Sbostic }
75846366Sbostic 
75946366Sbostic /*
76050997Sbostic  * If realloc guarantees that the pointer is not destroyed if the realloc
76150997Sbostic  * fails, then this routine can go away.
76250997Sbostic  */
76350997Sbostic static void *
76450997Sbostic hash_realloc(p_ptr, oldsize, newsize)
76550997Sbostic 	SEGMENT **p_ptr;
76650997Sbostic 	int oldsize, newsize;
76746366Sbostic {
76850997Sbostic 	register void *p;
76946366Sbostic 
77050997Sbostic 	if (p = malloc(newsize)) {
77150997Sbostic 		bcopy(*p_ptr, p, oldsize);
77250997Sbostic 		bzero(*p_ptr + oldsize, newsize - oldsize);
77346366Sbostic 		free(*p_ptr);
77446366Sbostic 		*p_ptr = p;
77546366Sbostic 	}
77646366Sbostic 	return (p);
77746366Sbostic }
77846366Sbostic 
77947251Sbostic extern u_int
78050997Sbostic __call_hash(k, len)
78150997Sbostic 	char *k;
78250997Sbostic 	int len;
78346366Sbostic {
78450997Sbostic 	int n, bucket;
78550997Sbostic 
78646366Sbostic 	n = hashp->hash(k, len);
78746366Sbostic 	bucket = n & hashp->HIGH_MASK;
78850997Sbostic 	if (bucket > hashp->MAX_BUCKET)
78950997Sbostic 		bucket = bucket & hashp->LOW_MASK;
79050997Sbostic 	return (bucket);
79146366Sbostic }
79246366Sbostic 
79346366Sbostic /*
79450997Sbostic  * Allocate segment table.  On error, destroy the table and set errno.
79550997Sbostic  *
79650997Sbostic  * Returns 0 on success
79750997Sbostic  */
79846366Sbostic static int
79950997Sbostic alloc_segs(nsegs)
80050997Sbostic 	int nsegs;
80146366Sbostic {
80250997Sbostic 	register int i;
80350997Sbostic 	register SEGMENT store;
80446366Sbostic 
80550997Sbostic 	int save_errno;
80646366Sbostic 
80750997Sbostic 	if (!(hashp->dir = calloc(hashp->DSIZE, sizeof(SEGMENT *)))) {
80850997Sbostic 		save_errno = errno;
80950997Sbostic 		(void)hdestroy();
81050997Sbostic 		errno = save_errno;
81150997Sbostic 		return (-1);
81250997Sbostic 	}
81350997Sbostic 	/* Allocate segments */
81450997Sbostic 	store = calloc(nsegs << hashp->SSHIFT, sizeof(SEGMENT));
81550997Sbostic 	if (!store) {
81650997Sbostic 		save_errno = errno;
81750997Sbostic 		(void)hdestroy();
81850997Sbostic 		errno = save_errno;
81950997Sbostic 		return (-1);
82050997Sbostic 	}
82150997Sbostic 	for (i = 0; i < nsegs; i++, hashp->nsegs++)
82250997Sbostic 		hashp->dir[i] = &store[i << hashp->SSHIFT];
82350997Sbostic 	return (0);
82446366Sbostic }
82546366Sbostic 
82650997Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
82746366Sbostic /*
82850997Sbostic  * Hashp->hdr needs to be byteswapped.
82950997Sbostic  */
83046366Sbostic static void
83150997Sbostic swap_header_copy(srcp, destp)
83250997Sbostic 	HASHHDR *srcp, *destp;
83346366Sbostic {
83450997Sbostic 	int i;
83546366Sbostic 
83650997Sbostic 	BLSWAP_COPY(srcp->magic, destp->magic);
83750997Sbostic 	BLSWAP_COPY(srcp->version, destp->version);
83850997Sbostic 	BLSWAP_COPY(srcp->lorder, destp->lorder);
83950997Sbostic 	BLSWAP_COPY(srcp->bsize, destp->bsize);
84050997Sbostic 	BLSWAP_COPY(srcp->bshift, destp->bshift);
84150997Sbostic 	BLSWAP_COPY(srcp->dsize, destp->dsize);
84250997Sbostic 	BLSWAP_COPY(srcp->ssize, destp->ssize);
84350997Sbostic 	BLSWAP_COPY(srcp->sshift, destp->sshift);
84450997Sbostic 	BLSWAP_COPY(srcp->max_bucket, destp->max_bucket);
84550997Sbostic 	BLSWAP_COPY(srcp->high_mask, destp->high_mask);
84650997Sbostic 	BLSWAP_COPY(srcp->low_mask, destp->low_mask);
84750997Sbostic 	BLSWAP_COPY(srcp->ffactor, destp->ffactor);
84850997Sbostic 	BLSWAP_COPY(srcp->nkeys, destp->nkeys);
84950997Sbostic 	BLSWAP_COPY(srcp->hdrpages, destp->hdrpages);
85050997Sbostic 	BLSWAP_COPY(srcp->h_charkey, destp->h_charkey);
85150997Sbostic 	for (i = 0; i < NCACHED; i++) {
85250997Sbostic 		BLSWAP_COPY(srcp->spares[i], destp->spares[i]);
85350997Sbostic 		BSSWAP_COPY(srcp->bitmaps[i], destp->bitmaps[i]);
85450997Sbostic 	}
85546366Sbostic }
85646366Sbostic 
85746366Sbostic static void
85850997Sbostic swap_header()
85946366Sbostic {
86050997Sbostic 	HASHHDR *hdrp;
86150997Sbostic 	int i;
86246366Sbostic 
86350997Sbostic 	hdrp = &hashp->hdr;
86446366Sbostic 
86550997Sbostic 	BLSWAP(hdrp->magic);
86650997Sbostic 	BLSWAP(hdrp->version);
86750997Sbostic 	BLSWAP(hdrp->lorder);
86850997Sbostic 	BLSWAP(hdrp->bsize);
86950997Sbostic 	BLSWAP(hdrp->bshift);
87050997Sbostic 	BLSWAP(hdrp->dsize);
87150997Sbostic 	BLSWAP(hdrp->ssize);
87250997Sbostic 	BLSWAP(hdrp->sshift);
87350997Sbostic 	BLSWAP(hdrp->max_bucket);
87450997Sbostic 	BLSWAP(hdrp->high_mask);
87550997Sbostic 	BLSWAP(hdrp->low_mask);
87650997Sbostic 	BLSWAP(hdrp->ffactor);
87750997Sbostic 	BLSWAP(hdrp->nkeys);
87850997Sbostic 	BLSWAP(hdrp->hdrpages);
87950997Sbostic 	BLSWAP(hdrp->h_charkey);
88050997Sbostic 	for (i = 0; i < NCACHED; i++) {
88150997Sbostic 		BLSWAP(hdrp->spares[i]);
88250997Sbostic 		BSSWAP(hdrp->bitmaps[i]);
88350997Sbostic 	}
88446366Sbostic }
88550997Sbostic #endif
886