xref: /csrg-svn/lib/libc/db/hash/hash.c (revision 55874)
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*55874Sbostic static char sccsid[] = "@(#)hash.c	5.25 (Berkeley) 08/07/92";
1346366Sbostic #endif /* LIBC_SCCS and not lint */
1446366Sbostic 
1546366Sbostic #include <sys/param.h>
1646366Sbostic #include <sys/stat.h>
1755452Sbostic 
1846562Sbostic #include <fcntl.h>
1946366Sbostic #include <errno.h>
2050997Sbostic #ifdef DEBUG
2146366Sbostic #include <assert.h>
2250997Sbostic #endif
2346366Sbostic #include <db.h>
2446562Sbostic #include <stdio.h>
2546562Sbostic #include <stdlib.h>
2646500Sbostic #include <unistd.h>
2746562Sbostic #include <string.h>
2846366Sbostic #include "hash.h"
2950997Sbostic #include "page.h"
3050997Sbostic #include "extern.h"
3146366Sbostic 
3250997Sbostic static int   alloc_segs __P((int));
3350997Sbostic static int   flush_meta __P((void));
3450997Sbostic static int   hash_access __P((ACTION, DBT *, DBT *));
3550997Sbostic static int   hash_close __P((DB *));
3650997Sbostic static int   hash_delete __P((const DB *, const DBT *, u_int));
3751057Sbostic static int   hash_get __P((const DB *, const DBT *, DBT *, u_int));
3850997Sbostic static int   hash_put __P((const DB *, const DBT *, const DBT *, u_int));
3950997Sbostic static void *hash_realloc __P((SEGMENT **, int, int));
4050997Sbostic static int   hash_seq __P((const DB *, DBT *, DBT *, u_int));
4150997Sbostic static int   hash_sync __P((const DB *));
4250997Sbostic static int   hdestroy __P((void));
4350997Sbostic static HTAB *init_hash __P((HASHINFO *));
4450997Sbostic static int   init_htab __P((int));
4550997Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
4650997Sbostic static void  swap_header __P((void));
4750997Sbostic static void  swap_header_copy __P((HASHHDR *, HASHHDR *));
4850997Sbostic #endif
4950997Sbostic 
5046366Sbostic /* Fast arithmetic, relying on powers of 2, */
5150997Sbostic #define MOD(x, y)		((x) & ((y) - 1))
5246366Sbostic 
5350997Sbostic #define RETURN_ERROR(ERR, LOC)	{ save_errno = ERR; goto LOC; }
5446366Sbostic 
5546366Sbostic /* Return values */
5650997Sbostic #define	SUCCESS	 (0)
5750997Sbostic #define	ERROR	(-1)
5850997Sbostic #define	ABNORMAL (1)
5946366Sbostic 
6046366Sbostic /* Local data */
6146366Sbostic HTAB *hashp = NULL;
6246366Sbostic 
6346366Sbostic #ifdef HASH_STATISTICS
6446366Sbostic long hash_accesses, hash_collisions, hash_expansions, hash_overflows;
6546366Sbostic #endif
6646366Sbostic 
6750997Sbostic /************************** INTERFACE ROUTINES ***************************/
6846366Sbostic /* OPEN/CLOSE */
6946366Sbostic 
7050997Sbostic extern DB *
7151072Sbostic __hash_open(file, flags, mode, info)
7250997Sbostic 	const char *file;
7350997Sbostic 	int flags, mode;
7450997Sbostic 	const HASHINFO *info;	/* Special directives for create */
7546366Sbostic {
7650997Sbostic 	struct stat statbuf;
7750997Sbostic 	DB *dbp;
7850997Sbostic 	int bpages, hdrsize, new_table, nsegs, save_errno;
7946366Sbostic 
8050997Sbostic 	if (!(hashp = calloc(1, sizeof(HTAB))))
8150997Sbostic 		return (NULL);
8250997Sbostic 	hashp->fp = -1;
8350997Sbostic 	/*
8450997Sbostic 	 * Select flags relevant to us. Even if user wants write only, we need
8550997Sbostic 	 * to be able to read the actual file, so we need to open it read/write.
8650997Sbostic 	 * But, the field in the hashp structure needs to be accurate so that
8750997Sbostic 	 * we can check accesses.
8850997Sbostic 	 */
8953603Sbostic 	hashp->flags = flags = flags & (O_CREAT | O_EXCL | O_EXLOCK |
9053603Sbostic 	    O_RDONLY | O_RDWR | O_SHLOCK | O_TRUNC | O_WRONLY);
9150997Sbostic 	if (flags & O_WRONLY)
9250997Sbostic 		flags = (flags & ~O_WRONLY) | O_RDWR;
9346366Sbostic 
9450997Sbostic 	new_table = 0;
9550997Sbostic 	if (!file || (flags & O_TRUNC) ||
9650997Sbostic 	    (stat(file, &statbuf) && (errno == ENOENT))) {
9750997Sbostic 		if (errno == ENOENT)
9850997Sbostic 			errno = 0; /* Just in case someone looks at errno */
9950997Sbostic 		new_table = 1;
10046485Sbostic 	}
10150997Sbostic 	if (file) {
10250997Sbostic 		if ((hashp->fp = open(file, flags, mode)) == -1)
10350997Sbostic 			RETURN_ERROR(errno, error0);
10450997Sbostic 		(void)fcntl(hashp->fp, F_SETFD, 1);
10546366Sbostic 	}
10650997Sbostic 	if (new_table) {
10750997Sbostic 		if (!(hashp = init_hash((HASHINFO *)info)))
10850997Sbostic 			RETURN_ERROR(errno, error1);
10950997Sbostic 	} else {
11050997Sbostic 		/* Table already exists */
11150997Sbostic 		if (info && info->hash)
11250997Sbostic 			hashp->hash = info->hash;
11350997Sbostic 		else
11450997Sbostic 			hashp->hash = default_hash;
11546366Sbostic 
11650997Sbostic 		hdrsize = read(hashp->fp, &hashp->hdr, sizeof(HASHHDR));
11746366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
11850997Sbostic 		swap_header();
11946366Sbostic #endif
12050997Sbostic 		if (hdrsize == -1)
12150997Sbostic 			RETURN_ERROR(errno, error1);
12250997Sbostic 		if (hdrsize != sizeof(HASHHDR))
12350997Sbostic 			RETURN_ERROR(EFTYPE, error1);
12450997Sbostic 		/* Verify file type, versions and hash function */
12550997Sbostic 		if (hashp->MAGIC != HASHMAGIC)
12650997Sbostic 			RETURN_ERROR(EFTYPE, error1);
12751061Sbostic 		if (hashp->VERSION != HASHVERSION)
12850997Sbostic 			RETURN_ERROR(EFTYPE, error1);
12950997Sbostic 		if (hashp->hash(CHARKEY, sizeof(CHARKEY)) != hashp->H_CHARKEY)
13050997Sbostic 			RETURN_ERROR(EFTYPE, error1);
13151057Sbostic 		/*
13251057Sbostic 		 * Figure out how many segments we need.  Max_Bucket is the
13351057Sbostic 		 * maximum bucket number, so the number of buckets is
13451057Sbostic 		 * max_bucket + 1.
13551057Sbostic 		 */
13651057Sbostic 		nsegs = (hashp->MAX_BUCKET + 1 + hashp->SGSIZE - 1) /
13750997Sbostic 			 hashp->SGSIZE;
13850997Sbostic 		hashp->nsegs = 0;
13950997Sbostic 		if (alloc_segs(nsegs))
14050997Sbostic 			/*
14150997Sbostic 			 * If alloc_segs fails, table will have been destroyed
14250997Sbostic 			 * and errno will have been set.
14350997Sbostic 			 */
14450997Sbostic 			return (NULL);
14550997Sbostic 		/* Read in bitmaps */
14651061Sbostic 		bpages = (hashp->SPARES[hashp->OVFL_POINT] +
14750997Sbostic 		    (hashp->BSIZE << BYTE_SHIFT) - 1) >>
14850997Sbostic 		    (hashp->BSHIFT + BYTE_SHIFT);
14946366Sbostic 
15050997Sbostic 		hashp->nmaps = bpages;
15151057Sbostic 		(void)memset(&hashp->mapp[0], 0, bpages * sizeof(u_long *));
15246366Sbostic 	}
15346366Sbostic 
15450997Sbostic 	/* Initialize Buffer Manager */
15550997Sbostic 	if (info && info->cachesize)
15650997Sbostic 		__buf_init(info->cachesize);
15750997Sbostic 	else
15850997Sbostic 		__buf_init(DEF_BUFSIZE);
15950997Sbostic 
16050997Sbostic 	hashp->new_file = new_table;
16150997Sbostic 	hashp->save_file = file && (hashp->flags & (O_WRONLY | O_RDWR));
16250997Sbostic 	hashp->cbucket = -1;
16350997Sbostic 	if (!(dbp = malloc(sizeof(DB)))) {
16450997Sbostic 		save_errno = errno;
16550997Sbostic 		hdestroy();
16650997Sbostic 		errno = save_errno;
16750997Sbostic 		return (NULL);
16846366Sbostic 	}
16950997Sbostic 	dbp->internal = (char *)hashp;
17050997Sbostic 	dbp->close = hash_close;
17150997Sbostic 	dbp->del = hash_delete;
17250997Sbostic 	dbp->get = hash_get;
17350997Sbostic 	dbp->put = hash_put;
17450997Sbostic 	dbp->seq = hash_seq;
17550997Sbostic 	dbp->sync = hash_sync;
17650997Sbostic 	dbp->type = DB_HASH;
17746366Sbostic 
17846366Sbostic #ifdef DEBUG
17950997Sbostic 	(void)fprintf(stderr,
18051061Sbostic "%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",
18150997Sbostic 	    "init_htab:",
18250997Sbostic 	    "TABLE POINTER   ", hashp,
18350997Sbostic 	    "BUCKET SIZE     ", hashp->BSIZE,
18450997Sbostic 	    "BUCKET SHIFT    ", hashp->BSHIFT,
18550997Sbostic 	    "DIRECTORY SIZE  ", hashp->DSIZE,
18650997Sbostic 	    "SEGMENT SIZE    ", hashp->SGSIZE,
18750997Sbostic 	    "SEGMENT SHIFT   ", hashp->SSHIFT,
18850997Sbostic 	    "FILL FACTOR     ", hashp->FFACTOR,
18950997Sbostic 	    "MAX BUCKET      ", hashp->MAX_BUCKET,
19051061Sbostic 	    "OVFL POINT	     ", hashp->OVFL_POINT,
19151061Sbostic 	    "LAST FREED      ", hashp->LAST_FREED,
19250997Sbostic 	    "HIGH MASK       ", hashp->HIGH_MASK,
19350997Sbostic 	    "LOW  MASK       ", hashp->LOW_MASK,
19450997Sbostic 	    "NSEGS           ", hashp->nsegs,
19550997Sbostic 	    "NKEYS           ", hashp->NKEYS);
19646366Sbostic #endif
19746366Sbostic #ifdef HASH_STATISTICS
19846366Sbostic 	hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0;
19946366Sbostic #endif
20050997Sbostic 	return (dbp);
20146366Sbostic 
20246366Sbostic error1:
20351192Sbostic 	if (hashp != NULL)
20451192Sbostic 		(void)close(hashp->fp);
20546366Sbostic 
20646366Sbostic error0:
20750997Sbostic 	free(hashp);
20850997Sbostic 	errno = save_errno;
20950997Sbostic 	return (NULL);
21046366Sbostic }
21146366Sbostic 
21246366Sbostic static int
21350997Sbostic hash_close(dbp)
21450997Sbostic 	DB *dbp;
21546366Sbostic {
21650997Sbostic 	int retval;
21746457Sbostic 
21850997Sbostic 	if (!dbp)
21950997Sbostic 		return (ERROR);
22046366Sbostic 	hashp = (HTAB *)dbp->internal;
22146457Sbostic 	retval = hdestroy();
22250997Sbostic 	free(dbp);
22350997Sbostic 	return (retval);
22446366Sbostic }
22546366Sbostic 
22646366Sbostic /************************** LOCAL CREATION ROUTINES **********************/
22750997Sbostic static HTAB *
22850997Sbostic init_hash(info)
22950997Sbostic 	HASHINFO *info;
23046366Sbostic {
23150997Sbostic 	int nelem;
23246366Sbostic 
23346366Sbostic 	nelem = 1;
23451061Sbostic 	hashp->NKEYS = 0;
23550997Sbostic 	hashp->LORDER = BYTE_ORDER;
23650997Sbostic 	hashp->BSIZE = DEF_BUCKET_SIZE;
23750997Sbostic 	hashp->BSHIFT = DEF_BUCKET_SHIFT;
23850997Sbostic 	hashp->SGSIZE = DEF_SEGSIZE;
23950997Sbostic 	hashp->SSHIFT = DEF_SEGSIZE_SHIFT;
24050997Sbostic 	hashp->DSIZE = DEF_DIRSIZE;
24150997Sbostic 	hashp->FFACTOR = DEF_FFACTOR;
24250997Sbostic 	hashp->hash = default_hash;
24350997Sbostic 	bzero(hashp->SPARES, sizeof(hashp->SPARES));
24451061Sbostic 	bzero (hashp->BITMAPS, sizeof (hashp->BITMAPS));
24546366Sbostic 
24650997Sbostic 	if (info) {
24750997Sbostic 		if (info->bsize) {
24850997Sbostic 			/* Round pagesize up to power of 2 */
24950997Sbostic 			hashp->BSHIFT = __log2(info->bsize);
25050997Sbostic 			hashp->BSIZE = 1 << hashp->BSHIFT;
25150997Sbostic 			if (hashp->BSIZE > MAX_BSIZE) {
25250997Sbostic 				errno = EINVAL;
25350997Sbostic 				return (NULL);
25450997Sbostic 			}
25546366Sbostic 		}
25650997Sbostic 		if (info->ffactor)
25750997Sbostic 			hashp->FFACTOR = info->ffactor;
25850997Sbostic 		if (info->hash)
25950997Sbostic 			hashp->hash = info->hash;
26050997Sbostic 		if (info->nelem)
26150997Sbostic 			nelem = info->nelem;
26250997Sbostic 		if (info->lorder) {
26350997Sbostic 			if (info->lorder != BIG_ENDIAN &&
26450997Sbostic 			    info->lorder != LITTLE_ENDIAN) {
26550997Sbostic 				errno = EINVAL;
26650997Sbostic 				return (NULL);
26750997Sbostic 			}
26850997Sbostic 			hashp->LORDER = info->lorder;
26946366Sbostic 		}
27046366Sbostic 	}
27146366Sbostic 	/* init_htab should destroy the table and set errno if it fails */
27250997Sbostic 	if (init_htab(nelem))
27350997Sbostic 		return (NULL);
27450997Sbostic 	else
27550997Sbostic 		return (hashp);
27646366Sbostic }
27746366Sbostic /*
27850997Sbostic  * This calls alloc_segs which may run out of memory.  Alloc_segs will destroy
27950997Sbostic  * the table and set errno, so we just pass the error information along.
28050997Sbostic  *
28150997Sbostic  * Returns 0 on No Error
28250997Sbostic  */
28346366Sbostic static int
28450997Sbostic init_htab(nelem)
28550997Sbostic 	int nelem;
28646366Sbostic {
28750997Sbostic 	register int nbuckets, nsegs;
28850997Sbostic 	int l2;
28946366Sbostic 
29050997Sbostic 	/*
29150997Sbostic 	 * Divide number of elements by the fill factor and determine a
29250997Sbostic 	 * desired number of buckets.  Allocate space for the next greater
29350997Sbostic 	 * power of two number of buckets.
29450997Sbostic 	 */
29546366Sbostic 	nelem = (nelem - 1) / hashp->FFACTOR + 1;
29646366Sbostic 
29752001Sbostic 	l2 = __log2(MAX(nelem, 2));
29846366Sbostic 	nbuckets = 1 << l2;
29946366Sbostic 
30046366Sbostic 	hashp->SPARES[l2] = l2 + 1;
30150997Sbostic 	hashp->SPARES[l2 + 1] = l2 + 1;
30251061Sbostic 	hashp->OVFL_POINT = l2;
30351061Sbostic 	hashp->LAST_FREED = 2;
30451061Sbostic 
30550997Sbostic 	/* First bitmap page is at: splitpoint l2 page offset 1 */
30651057Sbostic 	if (__init_bitmap(OADDR_OF(l2, 1), l2 + 1, 0))
30751057Sbostic 		return (-1);
30846366Sbostic 
30946366Sbostic 	hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1;
31046366Sbostic 	hashp->HIGH_MASK = (nbuckets << 1) - 1;
31150997Sbostic 	hashp->HDRPAGES = ((MAX(sizeof(HASHHDR), MINHDRSIZE) - 1) >>
31250997Sbostic 	    hashp->BSHIFT) + 1;
31346366Sbostic 
31446366Sbostic 	nsegs = (nbuckets - 1) / hashp->SGSIZE + 1;
31546366Sbostic 	nsegs = 1 << __log2(nsegs);
31646366Sbostic 
31750997Sbostic 	if (nsegs > hashp->DSIZE)
31850997Sbostic 		hashp->DSIZE = nsegs;
31950997Sbostic 	return (alloc_segs(nsegs));
32046366Sbostic }
32146366Sbostic 
32246366Sbostic /********************** DESTROY/CLOSE ROUTINES ************************/
32346366Sbostic 
32446366Sbostic /*
32550997Sbostic  * Flushes any changes to the file if necessary and destroys the hashp
32650997Sbostic  * structure, freeing all allocated space.
32750997Sbostic  */
32846366Sbostic static int
32946366Sbostic hdestroy()
33046366Sbostic {
33150997Sbostic 	int i, save_errno;
33246366Sbostic 
33346366Sbostic 	save_errno = 0;
33446366Sbostic 
33546366Sbostic 	if (hashp != NULL) {
33646366Sbostic #ifdef HASH_STATISTICS
33750997Sbostic 		(void)fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n",
33850997Sbostic 		    hash_accesses, hash_collisions);
33950997Sbostic 		(void)fprintf(stderr, "hdestroy: expansions %ld\n",
34050997Sbostic 		    hash_expansions);
34150997Sbostic 		(void)fprintf(stderr, "hdestroy: overflows %ld\n",
34250997Sbostic 		    hash_overflows);
34350997Sbostic 		(void)fprintf(stderr, "keys %ld maxp %d segmentcount %d\n",
34450997Sbostic 		    hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs);
34546366Sbostic 
34650997Sbostic 		for (i = 0; i < NCACHED; i++)
34750997Sbostic 			(void)fprintf(stderr,
34850997Sbostic 			    "spares[%d] = %d\n", i, hashp->SPARES[i]);
34946366Sbostic #endif
35050997Sbostic 		/*
35150997Sbostic 		 * Call on buffer manager to free buffers, and if required,
35250997Sbostic 		 * write them to disk.
35350997Sbostic 		 */
35450997Sbostic 		if (__buf_free(1, hashp->save_file))
35550997Sbostic 			save_errno = errno;
35650997Sbostic 		if (hashp->dir) {
35750997Sbostic 			free(*hashp->dir);	/* Free initial segments */
35850997Sbostic 			/* Free extra segments */
35950997Sbostic 			while (hashp->exsegs--)
36050997Sbostic 				free(hashp->dir[--hashp->nsegs]);
36150997Sbostic 			free(hashp->dir);
36246366Sbostic 		}
36350997Sbostic 		if (flush_meta() && !save_errno)
36450997Sbostic 			save_errno = errno;
36547251Sbostic 		/* Free Bigmaps */
36650997Sbostic 		for (i = 0; i < hashp->nmaps; i++)
36750997Sbostic 			if (hashp->mapp[i])
36850997Sbostic 				free(hashp->mapp[i]);
36946503Sbostic 
37050997Sbostic 		if (hashp->fp != -1)
37150997Sbostic 			(void)close(hashp->fp);
37250997Sbostic 		free(hashp);
37346366Sbostic 		hashp = NULL;
37446366Sbostic 	}
37550997Sbostic 	if (save_errno) {
37650997Sbostic 		errno = save_errno;
37750997Sbostic 		return (ERROR);
37846366Sbostic 	}
37950997Sbostic 	return (SUCCESS);
38046366Sbostic }
38150997Sbostic /*
38250997Sbostic  * Write modified pages to disk
38350997Sbostic  *
38450997Sbostic  * Returns:
38550997Sbostic  *	 0 == OK
38650997Sbostic  *	-1 ERROR
38750997Sbostic  */
38846366Sbostic static int
38946366Sbostic hash_sync(dbp)
39050997Sbostic 	const DB *dbp;
39146366Sbostic {
39250997Sbostic 	if (!dbp)
39350997Sbostic 		return (ERROR);
39446366Sbostic 	hashp = (HTAB *)dbp->internal;
39546366Sbostic 
39650997Sbostic 	if (!hashp->save_file)
39750997Sbostic 		return (0);
39850997Sbostic 	if (__buf_free(0, 1) || flush_meta())
39950997Sbostic 		return (ERROR);
40046366Sbostic 	hashp->new_file = 0;
40150997Sbostic 	return (0);
40246366Sbostic }
40346366Sbostic 
40446366Sbostic /*
40550997Sbostic  * Returns:
40650997Sbostic  *	 0 == OK
40750997Sbostic  *	-1 indicates that errno should be set
40850997Sbostic  */
40946366Sbostic static int
41046366Sbostic flush_meta()
41146366Sbostic {
41251814Smarc 	HASHHDR *whdrp;
41351814Smarc #if BYTE_ORDER == LITTLE_ENDIAN
41451814Smarc 	HASHHDR whdr;
41551814Smarc #endif
41650997Sbostic 	int fp, i, wsize;
41746366Sbostic 
41850997Sbostic 	if (!hashp->save_file)
41950997Sbostic 		return (0);
42046366Sbostic 	hashp->MAGIC = HASHMAGIC;
42151061Sbostic 	hashp->VERSION = HASHVERSION;
42250997Sbostic 	hashp->H_CHARKEY = hashp->hash(CHARKEY, sizeof(CHARKEY));
42346366Sbostic 
42446366Sbostic 	fp = hashp->fp;
42546366Sbostic 	whdrp = &hashp->hdr;
42646366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
42746366Sbostic 	whdrp = &whdr;
42850997Sbostic 	swap_header_copy(&hashp->hdr, whdrp);
42946366Sbostic #endif
43050997Sbostic 	if ((lseek(fp, 0, SEEK_SET) == -1) ||
43150997Sbostic 	    ((wsize = write(fp, whdrp, sizeof(HASHHDR))) == -1))
43250997Sbostic 		return (-1);
43350997Sbostic 	else
43450997Sbostic 		if (wsize != sizeof(HASHHDR)) {
43550997Sbostic 			errno = EFTYPE;
43650997Sbostic 			hashp->errno = errno;
43750997Sbostic 			return (-1);
43846366Sbostic 		}
43950997Sbostic 	for (i = 0; i < NCACHED; i++)
44050997Sbostic 		if (hashp->mapp[i])
44151072Sbostic 			if (__put_page((char *)hashp->mapp[i],
44250997Sbostic 				hashp->BITMAPS[i], 0, 1))
44350997Sbostic 				return (-1);
44450997Sbostic 	return (0);
44546366Sbostic }
44650997Sbostic 
44746366Sbostic /*******************************SEARCH ROUTINES *****************************/
44846366Sbostic /*
44950997Sbostic  * All the access routines return
45050997Sbostic  *
45150997Sbostic  * Returns:
45250997Sbostic  *	 0 on SUCCESS
45350997Sbostic  *	 1 to indicate an external ERROR (i.e. key not found, etc)
45450997Sbostic  *	-1 to indicate an internal ERROR (i.e. out of memory, etc)
45550997Sbostic  */
45646366Sbostic static int
45750997Sbostic hash_get(dbp, key, data, flag)
45850997Sbostic 	const DB *dbp;
45951057Sbostic 	const DBT *key;
46051057Sbostic 	DBT *data;
46150997Sbostic 	u_int flag;
46246366Sbostic {
46350997Sbostic 	if (flag) {
46450997Sbostic 		hashp->errno = errno = EINVAL;
46550997Sbostic 		return (ERROR);
46650997Sbostic 	}
46750997Sbostic 	hashp = (HTAB *)dbp->internal;
46850997Sbostic 	if (hashp->flags & O_WRONLY) {
46950997Sbostic 		hashp->errno = errno = EPERM;
47050997Sbostic 		return (ERROR);
47150997Sbostic 	}
47251057Sbostic 	return (hash_access(HASH_GET, (DBT *)key, data));
47346366Sbostic }
47446366Sbostic 
47546366Sbostic static int
47650997Sbostic hash_put(dbp, key, data, flag)
47750997Sbostic 	const DB *dbp;
47850997Sbostic 	const DBT *key, *data;
47950997Sbostic 	u_int flag;
48046366Sbostic {
48150997Sbostic 	if (flag && flag != R_NOOVERWRITE) {
48250997Sbostic 		hashp->errno = errno = EINVAL;
48350997Sbostic 		return (ERROR);
48450997Sbostic 	}
48550997Sbostic 	hashp = (HTAB *)dbp->internal;
48650997Sbostic 	if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
48750997Sbostic 		hashp->errno = errno = EPERM;
48850997Sbostic 		return (ERROR);
48950997Sbostic 	}
49051057Sbostic 	return (hash_access(flag == R_NOOVERWRITE ?
49150997Sbostic 	    HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data));
49246366Sbostic }
49346366Sbostic 
49446366Sbostic static int
49550997Sbostic hash_delete(dbp, key, flag)
49650997Sbostic 	const DB *dbp;
49750997Sbostic 	const DBT *key;
49850997Sbostic 	u_int flag;		/* Ignored */
49946366Sbostic {
50050997Sbostic 	if (flag && flag != R_CURSOR) {
50150997Sbostic 		hashp->errno = errno = EINVAL;
50250997Sbostic 		return (ERROR);
50350997Sbostic 	}
50450997Sbostic 	hashp = (HTAB *)dbp->internal;
50550997Sbostic 	if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
50650997Sbostic 		hashp->errno = errno = EPERM;
50750997Sbostic 		return (ERROR);
50850997Sbostic 	}
50950997Sbostic 	return (hash_access(HASH_DELETE, (DBT *)key, NULL));
51046366Sbostic }
51146366Sbostic 
51246366Sbostic /*
51350997Sbostic  * Assume that hashp has been set in wrapper routine.
51450997Sbostic  */
51546366Sbostic static int
51646366Sbostic hash_access(action, key, val)
51750997Sbostic 	ACTION action;
51850997Sbostic 	DBT *key, *val;
51946366Sbostic {
52050997Sbostic 	register BUFHEAD *rbufp;
52150997Sbostic 	BUFHEAD *bufp, *save_bufp;
52250997Sbostic 	register u_short *bp;
52350997Sbostic 	register int n, ndx, off, size;
52450997Sbostic 	register char *kp;
52550997Sbostic 	u_short pageno;
52646366Sbostic 
52746366Sbostic #ifdef HASH_STATISTICS
52846366Sbostic 	hash_accesses++;
52946366Sbostic #endif
53046366Sbostic 
53150997Sbostic 	off = hashp->BSIZE;
53246366Sbostic 	size = key->size;
53346950Sbostic 	kp = (char *)key->data;
53450997Sbostic 	rbufp = __get_buf(__call_hash(kp, size), NULL, 0);
53550997Sbostic 	if (!rbufp)
53650997Sbostic 		return (ERROR);
53746457Sbostic 	save_bufp = rbufp;
53846366Sbostic 
53946457Sbostic 	/* Pin the bucket chain */
54046457Sbostic 	rbufp->flags |= BUF_PIN;
54150997Sbostic 	for (bp = (u_short *)rbufp->page, n = *bp++, ndx = 1; ndx < n;)
54250997Sbostic 		if (bp[1] >= REAL_KEY) {
54350997Sbostic 			/* Real key/data pair */
54450997Sbostic 			if (size == off - *bp &&
54550997Sbostic 			    bcmp(kp, rbufp->page + *bp, size) == 0)
54650997Sbostic 				goto found;
54750997Sbostic 			off = bp[1];
54846366Sbostic #ifdef HASH_STATISTICS
54950997Sbostic 			hash_collisions++;
55046366Sbostic #endif
55150997Sbostic 			bp += 2;
55250997Sbostic 			ndx += 2;
55350997Sbostic 		} else if (bp[1] == OVFLPAGE) {
55450997Sbostic 			rbufp = __get_buf(*bp, rbufp, 0);
55550997Sbostic 			if (!rbufp) {
55650997Sbostic 				save_bufp->flags &= ~BUF_PIN;
55750997Sbostic 				return (ERROR);
55850997Sbostic 			}
55950997Sbostic 			/* FOR LOOP INIT */
56050997Sbostic 			bp = (u_short *)rbufp->page;
56150997Sbostic 			n = *bp++;
56250997Sbostic 			ndx = 1;
56350997Sbostic 			off = hashp->BSIZE;
56450997Sbostic 		} else if (bp[1] < REAL_KEY) {
56550997Sbostic 			if ((ndx = __find_bigpair(rbufp, ndx, kp, size)) > 0)
56650997Sbostic 				goto found;
56750997Sbostic 			if (ndx == -2) {
56850997Sbostic 				bufp = rbufp;
56950997Sbostic 				if (!(pageno = __find_last_page(&bufp))) {
57050997Sbostic 					ndx = 0;
57150997Sbostic 					rbufp = bufp;
57250997Sbostic 					break;	/* FOR */
57350997Sbostic 				}
57450997Sbostic 				rbufp = __get_buf(pageno, bufp, 0);
57550997Sbostic 				if (!rbufp) {
57650997Sbostic 					save_bufp->flags &= ~BUF_PIN;
57750997Sbostic 					return (ERROR);
57850997Sbostic 				}
57950997Sbostic 				/* FOR LOOP INIT */
58050997Sbostic 				bp = (u_short *)rbufp->page;
58150997Sbostic 				n = *bp++;
58250997Sbostic 				ndx = 1;
58350997Sbostic 				off = hashp->BSIZE;
58450997Sbostic 			} else {
58550997Sbostic 				save_bufp->flags &= ~BUF_PIN;
58650997Sbostic 				return (ERROR);
58750997Sbostic 			}
58846457Sbostic 		}
58946366Sbostic 
59046366Sbostic 	/* Not found */
59150997Sbostic 	switch (action) {
59250997Sbostic 	case HASH_PUT:
59350997Sbostic 	case HASH_PUTNEW:
59446366Sbostic 		if (__addel(rbufp, key, val)) {
59550997Sbostic 			save_bufp->flags &= ~BUF_PIN;
59650997Sbostic 			return (ERROR);
59746366Sbostic 		} else {
59850997Sbostic 			save_bufp->flags &= ~BUF_PIN;
59950997Sbostic 			return (SUCCESS);
60046366Sbostic 		}
60150997Sbostic 	case HASH_GET:
60250997Sbostic 	case HASH_DELETE:
60350997Sbostic 	default:
60446457Sbostic 		save_bufp->flags &= ~BUF_PIN;
60550997Sbostic 		return (ABNORMAL);
60646366Sbostic 	}
60746366Sbostic 
60846366Sbostic found:
60946366Sbostic 	switch (action) {
61050997Sbostic 	case HASH_PUTNEW:
61146457Sbostic 		save_bufp->flags &= ~BUF_PIN;
61250997Sbostic 		return (ABNORMAL);
61350997Sbostic 	case HASH_GET:
61446366Sbostic 		bp = (u_short *)rbufp->page;
61551072Sbostic 		if (bp[ndx + 1] < REAL_KEY) {
61651057Sbostic 			if (__big_return(rbufp, ndx, val, 0))
61751057Sbostic 				return (ERROR);
61851072Sbostic 		} else {
61950997Sbostic 			val->data = (u_char *)rbufp->page + (int)bp[ndx + 1];
62050997Sbostic 			val->size = bp[ndx] - bp[ndx + 1];
62146366Sbostic 		}
62246366Sbostic 		break;
62350997Sbostic 	case HASH_PUT:
62450997Sbostic 		if ((__delpair(rbufp, ndx)) || (__addel(rbufp, key, val))) {
62550997Sbostic 			save_bufp->flags &= ~BUF_PIN;
62650997Sbostic 			return (ERROR);
62746457Sbostic 		}
62846366Sbostic 		break;
62950997Sbostic 	case HASH_DELETE:
63050997Sbostic 		if (__delpair(rbufp, ndx))
63150997Sbostic 			return (ERROR);
63246366Sbostic 		break;
63346366Sbostic 	}
63446457Sbostic 	save_bufp->flags &= ~BUF_PIN;
63546366Sbostic 	return (SUCCESS);
63646366Sbostic }
63746366Sbostic 
63846366Sbostic static int
63946366Sbostic hash_seq(dbp, key, data, flag)
64050997Sbostic 	const DB *dbp;
64150997Sbostic 	DBT *key, *data;
64250997Sbostic 	u_int flag;
64346366Sbostic {
64450997Sbostic 	register u_int bucket;
64550997Sbostic 	register BUFHEAD *bufp;
64650997Sbostic 	u_short *bp, ndx;
64746366Sbostic 
64850997Sbostic 	if (flag && flag != R_FIRST && flag != R_NEXT) {
64950997Sbostic 		hashp->errno = errno = EINVAL;
65050997Sbostic 		return (ERROR);
65146366Sbostic 	}
65246366Sbostic 	hashp = (HTAB *)dbp->internal;
65350997Sbostic 	if (hashp->flags & O_WRONLY) {
65450997Sbostic 		hashp->errno = errno = EPERM;
65550997Sbostic 		return (ERROR);
65646366Sbostic 	}
65746366Sbostic #ifdef HASH_STATISTICS
65846366Sbostic 	hash_accesses++;
65946366Sbostic #endif
66050997Sbostic 	if ((hashp->cbucket < 0) || (flag == R_FIRST)) {
66150997Sbostic 		hashp->cbucket = 0;
66250997Sbostic 		hashp->cndx = 1;
66350997Sbostic 		hashp->cpage = NULL;
66446366Sbostic 	}
66546366Sbostic 
666*55874Sbostic 	for (bp = NULL; !bp || !bp[0]; ) {
66755452Sbostic 		if (!(bufp = hashp->cpage)) {
66855452Sbostic 			for (bucket = hashp->cbucket;
66955452Sbostic 			    bucket <= hashp->MAX_BUCKET;
67055452Sbostic 			    bucket++, hashp->cndx = 1) {
67155452Sbostic 				bufp = __get_buf(bucket, NULL, 0);
67255452Sbostic 				if (!bufp)
67355452Sbostic 					return (ERROR);
67455452Sbostic 				hashp->cpage = bufp;
67555452Sbostic 				bp = (u_short *)bufp->page;
67655452Sbostic 				if (bp[0])
67755452Sbostic 					break;
67855452Sbostic 			}
67955452Sbostic 			hashp->cbucket = bucket;
68055452Sbostic 			if (hashp->cbucket > hashp->MAX_BUCKET) {
68155452Sbostic 				hashp->cbucket = -1;
68255452Sbostic 				return (ABNORMAL);
68355452Sbostic 			}
68455452Sbostic 		} else
68555452Sbostic 			bp = (u_short *)hashp->cpage->page;
68655452Sbostic 
68755452Sbostic #ifdef DEBUG
68855452Sbostic 		assert(bp);
68955452Sbostic 		assert(bufp);
69055452Sbostic #endif
69155452Sbostic 		while (bp[hashp->cndx + 1] == OVFLPAGE) {
69255452Sbostic 			bufp = hashp->cpage =
69355452Sbostic 			    __get_buf(bp[hashp->cndx], bufp, 0);
69450997Sbostic 			if (!bufp)
69550997Sbostic 				return (ERROR);
69655452Sbostic 			bp = (u_short *)(bufp->page);
69755452Sbostic 			hashp->cndx = 1;
69850997Sbostic 		}
699*55874Sbostic 		if (!bp[0]) {
70055452Sbostic 			hashp->cpage = NULL;
701*55874Sbostic 			++hashp->cbucket;
702*55874Sbostic 		}
70346366Sbostic 	}
70446366Sbostic 	ndx = hashp->cndx;
70550997Sbostic 	if (bp[ndx + 1] < REAL_KEY) {
70651057Sbostic 		if (__big_keydata(bufp, key, data, 1))
70750997Sbostic 			return (ERROR);
70846366Sbostic 	} else {
70950997Sbostic 		key->data = (u_char *)hashp->cpage->page + bp[ndx];
71050997Sbostic 		key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx];
71150997Sbostic 		data->data = (u_char *)hashp->cpage->page + bp[ndx + 1];
71250997Sbostic 		data->size = bp[ndx] - bp[ndx + 1];
71350997Sbostic 		ndx += 2;
71450997Sbostic 		if (ndx > bp[0]) {
71550997Sbostic 			hashp->cpage = NULL;
71650997Sbostic 			hashp->cbucket++;
71750997Sbostic 			hashp->cndx = 1;
71850997Sbostic 		} else
71950997Sbostic 			hashp->cndx = ndx;
72046366Sbostic 	}
72146366Sbostic 	return (SUCCESS);
72246366Sbostic }
72346366Sbostic 
72446366Sbostic /********************************* UTILITIES ************************/
72550997Sbostic 
72646366Sbostic /*
72750997Sbostic  * Returns:
72850997Sbostic  *	 0 ==> OK
72950997Sbostic  *	-1 ==> Error
73050997Sbostic  */
73146366Sbostic extern int
73246366Sbostic __expand_table()
73346366Sbostic {
73450997Sbostic 	u_int old_bucket, new_bucket;
73550997Sbostic 	int dirsize, new_segnum, spare_ndx;
73650997Sbostic 
73746366Sbostic #ifdef HASH_STATISTICS
73846366Sbostic 	hash_expansions++;
73946366Sbostic #endif
74046366Sbostic 	new_bucket = ++hashp->MAX_BUCKET;
74146366Sbostic 	old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK);
74246366Sbostic 
74346366Sbostic 	new_segnum = new_bucket >> hashp->SSHIFT;
74446366Sbostic 
74546366Sbostic 	/* Check if we need a new segment */
74650997Sbostic 	if (new_segnum >= hashp->nsegs) {
74750997Sbostic 		/* Check if we need to expand directory */
74850997Sbostic 		if (new_segnum >= hashp->DSIZE) {
74950997Sbostic 			/* Reallocate directory */
75050997Sbostic 			dirsize = hashp->DSIZE * sizeof(SEGMENT *);
75150997Sbostic 			if (!hash_realloc(&hashp->dir, dirsize, dirsize << 1))
75250997Sbostic 				return (-1);
75350997Sbostic 			hashp->DSIZE = dirsize << 1;
75446366Sbostic 		}
75550997Sbostic 		if (!(hashp->dir[new_segnum] =
75650997Sbostic 			calloc(hashp->SGSIZE, sizeof(SEGMENT))))
75750997Sbostic 			return (-1);
75850997Sbostic 		hashp->exsegs++;
75950997Sbostic 		hashp->nsegs++;
76046366Sbostic 	}
76146366Sbostic 	/*
76250997Sbostic 	 * If the split point is increasing (MAX_BUCKET's log base 2
76350997Sbostic 	 * * increases), we need to copy the current contents of the spare
76450997Sbostic 	 * split bucket to the next bucket.
76550997Sbostic 	 */
76652001Sbostic 	spare_ndx = __log2(hashp->MAX_BUCKET + 1);
76752001Sbostic 	if (spare_ndx > hashp->OVFL_POINT) {
76851061Sbostic 		hashp->SPARES[spare_ndx] = hashp->SPARES[hashp->OVFL_POINT];
76951061Sbostic 		hashp->OVFL_POINT = spare_ndx;
77051061Sbostic 	}
77151061Sbostic 
77250997Sbostic 	if (new_bucket > hashp->HIGH_MASK) {
77350997Sbostic 		/* Starting a new doubling */
77450997Sbostic 		hashp->LOW_MASK = hashp->HIGH_MASK;
77550997Sbostic 		hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK;
77646366Sbostic 	}
77750997Sbostic 	/* Relocate records to the new bucket */
77850997Sbostic 	return (__split_page(old_bucket, new_bucket));
77950997Sbostic }
78046366Sbostic 
78146366Sbostic /*
78250997Sbostic  * If realloc guarantees that the pointer is not destroyed if the realloc
78350997Sbostic  * fails, then this routine can go away.
78450997Sbostic  */
78550997Sbostic static void *
78650997Sbostic hash_realloc(p_ptr, oldsize, newsize)
78750997Sbostic 	SEGMENT **p_ptr;
78850997Sbostic 	int oldsize, newsize;
78946366Sbostic {
79050997Sbostic 	register void *p;
79146366Sbostic 
79250997Sbostic 	if (p = malloc(newsize)) {
79350997Sbostic 		bcopy(*p_ptr, p, oldsize);
79450997Sbostic 		bzero(*p_ptr + oldsize, newsize - oldsize);
79546366Sbostic 		free(*p_ptr);
79646366Sbostic 		*p_ptr = p;
79746366Sbostic 	}
79846366Sbostic 	return (p);
79946366Sbostic }
80046366Sbostic 
80147251Sbostic extern u_int
80250997Sbostic __call_hash(k, len)
80350997Sbostic 	char *k;
80450997Sbostic 	int len;
80546366Sbostic {
80650997Sbostic 	int n, bucket;
80750997Sbostic 
80846366Sbostic 	n = hashp->hash(k, len);
80946366Sbostic 	bucket = n & hashp->HIGH_MASK;
81050997Sbostic 	if (bucket > hashp->MAX_BUCKET)
81150997Sbostic 		bucket = bucket & hashp->LOW_MASK;
81250997Sbostic 	return (bucket);
81346366Sbostic }
81446366Sbostic 
81546366Sbostic /*
81650997Sbostic  * Allocate segment table.  On error, destroy the table and set errno.
81750997Sbostic  *
81850997Sbostic  * Returns 0 on success
81950997Sbostic  */
82046366Sbostic static int
82150997Sbostic alloc_segs(nsegs)
82250997Sbostic 	int nsegs;
82346366Sbostic {
82450997Sbostic 	register int i;
82550997Sbostic 	register SEGMENT store;
82646366Sbostic 
82750997Sbostic 	int save_errno;
82846366Sbostic 
82950997Sbostic 	if (!(hashp->dir = calloc(hashp->DSIZE, sizeof(SEGMENT *)))) {
83050997Sbostic 		save_errno = errno;
83150997Sbostic 		(void)hdestroy();
83250997Sbostic 		errno = save_errno;
83350997Sbostic 		return (-1);
83450997Sbostic 	}
83550997Sbostic 	/* Allocate segments */
83650997Sbostic 	store = calloc(nsegs << hashp->SSHIFT, sizeof(SEGMENT));
83750997Sbostic 	if (!store) {
83850997Sbostic 		save_errno = errno;
83950997Sbostic 		(void)hdestroy();
84050997Sbostic 		errno = save_errno;
84150997Sbostic 		return (-1);
84250997Sbostic 	}
84350997Sbostic 	for (i = 0; i < nsegs; i++, hashp->nsegs++)
84450997Sbostic 		hashp->dir[i] = &store[i << hashp->SSHIFT];
84550997Sbostic 	return (0);
84646366Sbostic }
84746366Sbostic 
84850997Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
84946366Sbostic /*
85050997Sbostic  * Hashp->hdr needs to be byteswapped.
85150997Sbostic  */
85246366Sbostic static void
85350997Sbostic swap_header_copy(srcp, destp)
85450997Sbostic 	HASHHDR *srcp, *destp;
85546366Sbostic {
85650997Sbostic 	int i;
85746366Sbostic 
85850997Sbostic 	BLSWAP_COPY(srcp->magic, destp->magic);
85950997Sbostic 	BLSWAP_COPY(srcp->version, destp->version);
86050997Sbostic 	BLSWAP_COPY(srcp->lorder, destp->lorder);
86150997Sbostic 	BLSWAP_COPY(srcp->bsize, destp->bsize);
86250997Sbostic 	BLSWAP_COPY(srcp->bshift, destp->bshift);
86350997Sbostic 	BLSWAP_COPY(srcp->dsize, destp->dsize);
86450997Sbostic 	BLSWAP_COPY(srcp->ssize, destp->ssize);
86550997Sbostic 	BLSWAP_COPY(srcp->sshift, destp->sshift);
86651061Sbostic 	BLSWAP_COPY(srcp->ovfl_point, destp->ovfl_point);
86751061Sbostic 	BLSWAP_COPY(srcp->last_freed, destp->last_freed);
86850997Sbostic 	BLSWAP_COPY(srcp->max_bucket, destp->max_bucket);
86950997Sbostic 	BLSWAP_COPY(srcp->high_mask, destp->high_mask);
87050997Sbostic 	BLSWAP_COPY(srcp->low_mask, destp->low_mask);
87150997Sbostic 	BLSWAP_COPY(srcp->ffactor, destp->ffactor);
87250997Sbostic 	BLSWAP_COPY(srcp->nkeys, destp->nkeys);
87350997Sbostic 	BLSWAP_COPY(srcp->hdrpages, destp->hdrpages);
87450997Sbostic 	BLSWAP_COPY(srcp->h_charkey, destp->h_charkey);
87550997Sbostic 	for (i = 0; i < NCACHED; i++) {
87650997Sbostic 		BLSWAP_COPY(srcp->spares[i], destp->spares[i]);
87750997Sbostic 		BSSWAP_COPY(srcp->bitmaps[i], destp->bitmaps[i]);
87850997Sbostic 	}
87946366Sbostic }
88046366Sbostic 
88146366Sbostic static void
88250997Sbostic swap_header()
88346366Sbostic {
88450997Sbostic 	HASHHDR *hdrp;
88550997Sbostic 	int i;
88646366Sbostic 
88750997Sbostic 	hdrp = &hashp->hdr;
88846366Sbostic 
88950997Sbostic 	BLSWAP(hdrp->magic);
89050997Sbostic 	BLSWAP(hdrp->version);
89150997Sbostic 	BLSWAP(hdrp->lorder);
89250997Sbostic 	BLSWAP(hdrp->bsize);
89350997Sbostic 	BLSWAP(hdrp->bshift);
89450997Sbostic 	BLSWAP(hdrp->dsize);
89550997Sbostic 	BLSWAP(hdrp->ssize);
89650997Sbostic 	BLSWAP(hdrp->sshift);
89751061Sbostic 	BLSWAP(hdrp->ovfl_point);
89851061Sbostic 	BLSWAP(hdrp->last_freed);
89950997Sbostic 	BLSWAP(hdrp->max_bucket);
90050997Sbostic 	BLSWAP(hdrp->high_mask);
90150997Sbostic 	BLSWAP(hdrp->low_mask);
90250997Sbostic 	BLSWAP(hdrp->ffactor);
90350997Sbostic 	BLSWAP(hdrp->nkeys);
90450997Sbostic 	BLSWAP(hdrp->hdrpages);
90550997Sbostic 	BLSWAP(hdrp->h_charkey);
90650997Sbostic 	for (i = 0; i < NCACHED; i++) {
90750997Sbostic 		BLSWAP(hdrp->spares[i]);
90850997Sbostic 		BSSWAP(hdrp->bitmaps[i]);
90950997Sbostic 	}
91046366Sbostic }
91150997Sbostic #endif
912