xref: /csrg-svn/lib/libc/db/hash/hash.c (revision 56669)
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*56669Sbostic static char sccsid[] = "@(#)hash.c	5.28 (Berkeley) 11/02/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 
8056661Sbostic 	if ((flags & O_ACCMODE) == O_WRONLY) {
8156422Smargo 		errno = EINVAL;
8256422Smargo 		return (NULL);
8356422Smargo 	}
8456422Smargo 
8550997Sbostic 	if (!(hashp = calloc(1, sizeof(HTAB))))
8650997Sbostic 		return (NULL);
8750997Sbostic 	hashp->fp = -1;
8850997Sbostic 	/*
8950997Sbostic 	 * Select flags relevant to us. Even if user wants write only, we need
9050997Sbostic 	 * to be able to read the actual file, so we need to open it read/write.
9150997Sbostic 	 * But, the field in the hashp structure needs to be accurate so that
9250997Sbostic 	 * we can check accesses.
9350997Sbostic 	 */
9453603Sbostic 	hashp->flags = flags = flags & (O_CREAT | O_EXCL | O_EXLOCK |
9556422Smargo 	    O_RDONLY | O_RDWR | O_SHLOCK | O_TRUNC);
9646366Sbostic 
9750997Sbostic 	new_table = 0;
9850997Sbostic 	if (!file || (flags & O_TRUNC) ||
9950997Sbostic 	    (stat(file, &statbuf) && (errno == ENOENT))) {
10050997Sbostic 		if (errno == ENOENT)
10150997Sbostic 			errno = 0; /* Just in case someone looks at errno */
10250997Sbostic 		new_table = 1;
10346485Sbostic 	}
10450997Sbostic 	if (file) {
10550997Sbostic 		if ((hashp->fp = open(file, flags, mode)) == -1)
10650997Sbostic 			RETURN_ERROR(errno, error0);
10750997Sbostic 		(void)fcntl(hashp->fp, F_SETFD, 1);
10846366Sbostic 	}
10950997Sbostic 	if (new_table) {
11050997Sbostic 		if (!(hashp = init_hash((HASHINFO *)info)))
11150997Sbostic 			RETURN_ERROR(errno, error1);
11250997Sbostic 	} else {
11350997Sbostic 		/* Table already exists */
11450997Sbostic 		if (info && info->hash)
11550997Sbostic 			hashp->hash = info->hash;
11650997Sbostic 		else
11750997Sbostic 			hashp->hash = default_hash;
11846366Sbostic 
11950997Sbostic 		hdrsize = read(hashp->fp, &hashp->hdr, sizeof(HASHHDR));
12046366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
12150997Sbostic 		swap_header();
12246366Sbostic #endif
12350997Sbostic 		if (hdrsize == -1)
12450997Sbostic 			RETURN_ERROR(errno, error1);
12550997Sbostic 		if (hdrsize != sizeof(HASHHDR))
12650997Sbostic 			RETURN_ERROR(EFTYPE, error1);
12750997Sbostic 		/* Verify file type, versions and hash function */
12850997Sbostic 		if (hashp->MAGIC != HASHMAGIC)
12950997Sbostic 			RETURN_ERROR(EFTYPE, error1);
13051061Sbostic 		if (hashp->VERSION != HASHVERSION)
13150997Sbostic 			RETURN_ERROR(EFTYPE, error1);
13250997Sbostic 		if (hashp->hash(CHARKEY, sizeof(CHARKEY)) != hashp->H_CHARKEY)
13350997Sbostic 			RETURN_ERROR(EFTYPE, error1);
13451057Sbostic 		/*
13551057Sbostic 		 * Figure out how many segments we need.  Max_Bucket is the
13651057Sbostic 		 * maximum bucket number, so the number of buckets is
13751057Sbostic 		 * max_bucket + 1.
13851057Sbostic 		 */
13951057Sbostic 		nsegs = (hashp->MAX_BUCKET + 1 + hashp->SGSIZE - 1) /
14050997Sbostic 			 hashp->SGSIZE;
14150997Sbostic 		hashp->nsegs = 0;
14250997Sbostic 		if (alloc_segs(nsegs))
14350997Sbostic 			/*
14450997Sbostic 			 * If alloc_segs fails, table will have been destroyed
14550997Sbostic 			 * and errno will have been set.
14650997Sbostic 			 */
14750997Sbostic 			return (NULL);
14850997Sbostic 		/* Read in bitmaps */
14951061Sbostic 		bpages = (hashp->SPARES[hashp->OVFL_POINT] +
15050997Sbostic 		    (hashp->BSIZE << BYTE_SHIFT) - 1) >>
15150997Sbostic 		    (hashp->BSHIFT + BYTE_SHIFT);
15246366Sbostic 
15350997Sbostic 		hashp->nmaps = bpages;
15451057Sbostic 		(void)memset(&hashp->mapp[0], 0, bpages * sizeof(u_long *));
15546366Sbostic 	}
15646366Sbostic 
15750997Sbostic 	/* Initialize Buffer Manager */
15850997Sbostic 	if (info && info->cachesize)
15950997Sbostic 		__buf_init(info->cachesize);
16050997Sbostic 	else
16150997Sbostic 		__buf_init(DEF_BUFSIZE);
16250997Sbostic 
16350997Sbostic 	hashp->new_file = new_table;
16456422Smargo 	hashp->save_file = file && (hashp->flags & O_RDWR);
16550997Sbostic 	hashp->cbucket = -1;
16650997Sbostic 	if (!(dbp = malloc(sizeof(DB)))) {
16750997Sbostic 		save_errno = errno;
16850997Sbostic 		hdestroy();
16950997Sbostic 		errno = save_errno;
17050997Sbostic 		return (NULL);
17146366Sbostic 	}
17250997Sbostic 	dbp->internal = (char *)hashp;
17350997Sbostic 	dbp->close = hash_close;
17450997Sbostic 	dbp->del = hash_delete;
17550997Sbostic 	dbp->get = hash_get;
17650997Sbostic 	dbp->put = hash_put;
17750997Sbostic 	dbp->seq = hash_seq;
17850997Sbostic 	dbp->sync = hash_sync;
17950997Sbostic 	dbp->type = DB_HASH;
18046366Sbostic 
18146366Sbostic #ifdef DEBUG
18250997Sbostic 	(void)fprintf(stderr,
18351061Sbostic "%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",
18450997Sbostic 	    "init_htab:",
18550997Sbostic 	    "TABLE POINTER   ", hashp,
18650997Sbostic 	    "BUCKET SIZE     ", hashp->BSIZE,
18750997Sbostic 	    "BUCKET SHIFT    ", hashp->BSHIFT,
18850997Sbostic 	    "DIRECTORY SIZE  ", hashp->DSIZE,
18950997Sbostic 	    "SEGMENT SIZE    ", hashp->SGSIZE,
19050997Sbostic 	    "SEGMENT SHIFT   ", hashp->SSHIFT,
19150997Sbostic 	    "FILL FACTOR     ", hashp->FFACTOR,
19250997Sbostic 	    "MAX BUCKET      ", hashp->MAX_BUCKET,
19351061Sbostic 	    "OVFL POINT	     ", hashp->OVFL_POINT,
19451061Sbostic 	    "LAST FREED      ", hashp->LAST_FREED,
19550997Sbostic 	    "HIGH MASK       ", hashp->HIGH_MASK,
19650997Sbostic 	    "LOW  MASK       ", hashp->LOW_MASK,
19750997Sbostic 	    "NSEGS           ", hashp->nsegs,
19850997Sbostic 	    "NKEYS           ", hashp->NKEYS);
19946366Sbostic #endif
20046366Sbostic #ifdef HASH_STATISTICS
20146366Sbostic 	hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0;
20246366Sbostic #endif
20350997Sbostic 	return (dbp);
20446366Sbostic 
20546366Sbostic error1:
20651192Sbostic 	if (hashp != NULL)
20751192Sbostic 		(void)close(hashp->fp);
20846366Sbostic 
20946366Sbostic error0:
21050997Sbostic 	free(hashp);
21150997Sbostic 	errno = save_errno;
21250997Sbostic 	return (NULL);
21346366Sbostic }
21446366Sbostic 
21546366Sbostic static int
21650997Sbostic hash_close(dbp)
21750997Sbostic 	DB *dbp;
21846366Sbostic {
21950997Sbostic 	int retval;
22046457Sbostic 
22150997Sbostic 	if (!dbp)
22250997Sbostic 		return (ERROR);
22346366Sbostic 	hashp = (HTAB *)dbp->internal;
22446457Sbostic 	retval = hdestroy();
22550997Sbostic 	free(dbp);
22650997Sbostic 	return (retval);
22746366Sbostic }
22846366Sbostic 
22946366Sbostic /************************** LOCAL CREATION ROUTINES **********************/
23050997Sbostic static HTAB *
23150997Sbostic init_hash(info)
23250997Sbostic 	HASHINFO *info;
23346366Sbostic {
23450997Sbostic 	int nelem;
23546366Sbostic 
23646366Sbostic 	nelem = 1;
23751061Sbostic 	hashp->NKEYS = 0;
23850997Sbostic 	hashp->LORDER = BYTE_ORDER;
23950997Sbostic 	hashp->BSIZE = DEF_BUCKET_SIZE;
24050997Sbostic 	hashp->BSHIFT = DEF_BUCKET_SHIFT;
24150997Sbostic 	hashp->SGSIZE = DEF_SEGSIZE;
24250997Sbostic 	hashp->SSHIFT = DEF_SEGSIZE_SHIFT;
24350997Sbostic 	hashp->DSIZE = DEF_DIRSIZE;
24450997Sbostic 	hashp->FFACTOR = DEF_FFACTOR;
24550997Sbostic 	hashp->hash = default_hash;
24650997Sbostic 	bzero(hashp->SPARES, sizeof(hashp->SPARES));
24751061Sbostic 	bzero (hashp->BITMAPS, sizeof (hashp->BITMAPS));
24846366Sbostic 
24950997Sbostic 	if (info) {
25050997Sbostic 		if (info->bsize) {
25150997Sbostic 			/* Round pagesize up to power of 2 */
25250997Sbostic 			hashp->BSHIFT = __log2(info->bsize);
25350997Sbostic 			hashp->BSIZE = 1 << hashp->BSHIFT;
25450997Sbostic 			if (hashp->BSIZE > MAX_BSIZE) {
25550997Sbostic 				errno = EINVAL;
25650997Sbostic 				return (NULL);
25750997Sbostic 			}
25846366Sbostic 		}
25950997Sbostic 		if (info->ffactor)
26050997Sbostic 			hashp->FFACTOR = info->ffactor;
26150997Sbostic 		if (info->hash)
26250997Sbostic 			hashp->hash = info->hash;
26350997Sbostic 		if (info->nelem)
26450997Sbostic 			nelem = info->nelem;
26550997Sbostic 		if (info->lorder) {
26650997Sbostic 			if (info->lorder != BIG_ENDIAN &&
26750997Sbostic 			    info->lorder != LITTLE_ENDIAN) {
26850997Sbostic 				errno = EINVAL;
26950997Sbostic 				return (NULL);
27050997Sbostic 			}
27150997Sbostic 			hashp->LORDER = info->lorder;
27246366Sbostic 		}
27346366Sbostic 	}
27446366Sbostic 	/* init_htab should destroy the table and set errno if it fails */
27550997Sbostic 	if (init_htab(nelem))
27650997Sbostic 		return (NULL);
27750997Sbostic 	else
27850997Sbostic 		return (hashp);
27946366Sbostic }
28046366Sbostic /*
28150997Sbostic  * This calls alloc_segs which may run out of memory.  Alloc_segs will destroy
28250997Sbostic  * the table and set errno, so we just pass the error information along.
28350997Sbostic  *
28450997Sbostic  * Returns 0 on No Error
28550997Sbostic  */
28646366Sbostic static int
28750997Sbostic init_htab(nelem)
28850997Sbostic 	int nelem;
28946366Sbostic {
29050997Sbostic 	register int nbuckets, nsegs;
29150997Sbostic 	int l2;
29246366Sbostic 
29350997Sbostic 	/*
29450997Sbostic 	 * Divide number of elements by the fill factor and determine a
29550997Sbostic 	 * desired number of buckets.  Allocate space for the next greater
29650997Sbostic 	 * power of two number of buckets.
29750997Sbostic 	 */
29846366Sbostic 	nelem = (nelem - 1) / hashp->FFACTOR + 1;
29946366Sbostic 
30052001Sbostic 	l2 = __log2(MAX(nelem, 2));
30146366Sbostic 	nbuckets = 1 << l2;
30246366Sbostic 
30346366Sbostic 	hashp->SPARES[l2] = l2 + 1;
30450997Sbostic 	hashp->SPARES[l2 + 1] = l2 + 1;
30551061Sbostic 	hashp->OVFL_POINT = l2;
30651061Sbostic 	hashp->LAST_FREED = 2;
30751061Sbostic 
30850997Sbostic 	/* First bitmap page is at: splitpoint l2 page offset 1 */
30951057Sbostic 	if (__init_bitmap(OADDR_OF(l2, 1), l2 + 1, 0))
31051057Sbostic 		return (-1);
31146366Sbostic 
31246366Sbostic 	hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1;
31346366Sbostic 	hashp->HIGH_MASK = (nbuckets << 1) - 1;
31450997Sbostic 	hashp->HDRPAGES = ((MAX(sizeof(HASHHDR), MINHDRSIZE) - 1) >>
31550997Sbostic 	    hashp->BSHIFT) + 1;
31646366Sbostic 
31746366Sbostic 	nsegs = (nbuckets - 1) / hashp->SGSIZE + 1;
31846366Sbostic 	nsegs = 1 << __log2(nsegs);
31946366Sbostic 
32050997Sbostic 	if (nsegs > hashp->DSIZE)
32150997Sbostic 		hashp->DSIZE = nsegs;
32250997Sbostic 	return (alloc_segs(nsegs));
32346366Sbostic }
32446366Sbostic 
32546366Sbostic /********************** DESTROY/CLOSE ROUTINES ************************/
32646366Sbostic 
32746366Sbostic /*
32850997Sbostic  * Flushes any changes to the file if necessary and destroys the hashp
32950997Sbostic  * structure, freeing all allocated space.
33050997Sbostic  */
33146366Sbostic static int
33246366Sbostic hdestroy()
33346366Sbostic {
33450997Sbostic 	int i, save_errno;
33546366Sbostic 
33646366Sbostic 	save_errno = 0;
33746366Sbostic 
33846366Sbostic 	if (hashp != NULL) {
33946366Sbostic #ifdef HASH_STATISTICS
34050997Sbostic 		(void)fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n",
34150997Sbostic 		    hash_accesses, hash_collisions);
34250997Sbostic 		(void)fprintf(stderr, "hdestroy: expansions %ld\n",
34350997Sbostic 		    hash_expansions);
34450997Sbostic 		(void)fprintf(stderr, "hdestroy: overflows %ld\n",
34550997Sbostic 		    hash_overflows);
34650997Sbostic 		(void)fprintf(stderr, "keys %ld maxp %d segmentcount %d\n",
34750997Sbostic 		    hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs);
34846366Sbostic 
34950997Sbostic 		for (i = 0; i < NCACHED; i++)
35050997Sbostic 			(void)fprintf(stderr,
35150997Sbostic 			    "spares[%d] = %d\n", i, hashp->SPARES[i]);
35246366Sbostic #endif
35350997Sbostic 		/*
35450997Sbostic 		 * Call on buffer manager to free buffers, and if required,
35550997Sbostic 		 * write them to disk.
35650997Sbostic 		 */
35750997Sbostic 		if (__buf_free(1, hashp->save_file))
35850997Sbostic 			save_errno = errno;
35950997Sbostic 		if (hashp->dir) {
36050997Sbostic 			free(*hashp->dir);	/* Free initial segments */
36150997Sbostic 			/* Free extra segments */
36250997Sbostic 			while (hashp->exsegs--)
36350997Sbostic 				free(hashp->dir[--hashp->nsegs]);
36450997Sbostic 			free(hashp->dir);
36546366Sbostic 		}
36650997Sbostic 		if (flush_meta() && !save_errno)
36750997Sbostic 			save_errno = errno;
36847251Sbostic 		/* Free Bigmaps */
36950997Sbostic 		for (i = 0; i < hashp->nmaps; i++)
37050997Sbostic 			if (hashp->mapp[i])
37150997Sbostic 				free(hashp->mapp[i]);
37246503Sbostic 
37350997Sbostic 		if (hashp->fp != -1)
37450997Sbostic 			(void)close(hashp->fp);
37550997Sbostic 		free(hashp);
37646366Sbostic 		hashp = NULL;
37746366Sbostic 	}
37850997Sbostic 	if (save_errno) {
37950997Sbostic 		errno = save_errno;
38050997Sbostic 		return (ERROR);
38146366Sbostic 	}
38250997Sbostic 	return (SUCCESS);
38346366Sbostic }
38450997Sbostic /*
38550997Sbostic  * Write modified pages to disk
38650997Sbostic  *
38750997Sbostic  * Returns:
38850997Sbostic  *	 0 == OK
38950997Sbostic  *	-1 ERROR
39050997Sbostic  */
39146366Sbostic static int
39246366Sbostic hash_sync(dbp)
39350997Sbostic 	const DB *dbp;
39446366Sbostic {
39550997Sbostic 	if (!dbp)
39650997Sbostic 		return (ERROR);
39746366Sbostic 	hashp = (HTAB *)dbp->internal;
39846366Sbostic 
39950997Sbostic 	if (!hashp->save_file)
40050997Sbostic 		return (0);
40150997Sbostic 	if (__buf_free(0, 1) || flush_meta())
40250997Sbostic 		return (ERROR);
40346366Sbostic 	hashp->new_file = 0;
40450997Sbostic 	return (0);
40546366Sbostic }
40646366Sbostic 
40746366Sbostic /*
40850997Sbostic  * Returns:
40950997Sbostic  *	 0 == OK
41050997Sbostic  *	-1 indicates that errno should be set
41150997Sbostic  */
41246366Sbostic static int
41346366Sbostic flush_meta()
41446366Sbostic {
41551814Smarc 	HASHHDR *whdrp;
41651814Smarc #if BYTE_ORDER == LITTLE_ENDIAN
41751814Smarc 	HASHHDR whdr;
41851814Smarc #endif
41950997Sbostic 	int fp, i, wsize;
42046366Sbostic 
42150997Sbostic 	if (!hashp->save_file)
42250997Sbostic 		return (0);
42346366Sbostic 	hashp->MAGIC = HASHMAGIC;
42451061Sbostic 	hashp->VERSION = HASHVERSION;
42550997Sbostic 	hashp->H_CHARKEY = hashp->hash(CHARKEY, sizeof(CHARKEY));
42646366Sbostic 
42746366Sbostic 	fp = hashp->fp;
42846366Sbostic 	whdrp = &hashp->hdr;
42946366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
43046366Sbostic 	whdrp = &whdr;
43150997Sbostic 	swap_header_copy(&hashp->hdr, whdrp);
43246366Sbostic #endif
433*56669Sbostic 	if ((lseek(fp, (off_t)0, SEEK_SET) == -1) ||
43450997Sbostic 	    ((wsize = write(fp, whdrp, sizeof(HASHHDR))) == -1))
43550997Sbostic 		return (-1);
43650997Sbostic 	else
43750997Sbostic 		if (wsize != sizeof(HASHHDR)) {
43850997Sbostic 			errno = EFTYPE;
43950997Sbostic 			hashp->errno = errno;
44050997Sbostic 			return (-1);
44146366Sbostic 		}
44250997Sbostic 	for (i = 0; i < NCACHED; i++)
44350997Sbostic 		if (hashp->mapp[i])
44451072Sbostic 			if (__put_page((char *)hashp->mapp[i],
44550997Sbostic 				hashp->BITMAPS[i], 0, 1))
44650997Sbostic 				return (-1);
44750997Sbostic 	return (0);
44846366Sbostic }
44950997Sbostic 
45046366Sbostic /*******************************SEARCH ROUTINES *****************************/
45146366Sbostic /*
45250997Sbostic  * All the access routines return
45350997Sbostic  *
45450997Sbostic  * Returns:
45550997Sbostic  *	 0 on SUCCESS
45650997Sbostic  *	 1 to indicate an external ERROR (i.e. key not found, etc)
45750997Sbostic  *	-1 to indicate an internal ERROR (i.e. out of memory, etc)
45850997Sbostic  */
45946366Sbostic static int
46050997Sbostic hash_get(dbp, key, data, flag)
46150997Sbostic 	const DB *dbp;
46251057Sbostic 	const DBT *key;
46351057Sbostic 	DBT *data;
46450997Sbostic 	u_int flag;
46546366Sbostic {
46650997Sbostic 	if (flag) {
46750997Sbostic 		hashp->errno = errno = EINVAL;
46850997Sbostic 		return (ERROR);
46950997Sbostic 	}
47050997Sbostic 	hashp = (HTAB *)dbp->internal;
47151057Sbostic 	return (hash_access(HASH_GET, (DBT *)key, data));
47246366Sbostic }
47346366Sbostic 
47446366Sbostic static int
47550997Sbostic hash_put(dbp, key, data, flag)
47650997Sbostic 	const DB *dbp;
47750997Sbostic 	const DBT *key, *data;
47850997Sbostic 	u_int flag;
47946366Sbostic {
48050997Sbostic 	if (flag && flag != R_NOOVERWRITE) {
48150997Sbostic 		hashp->errno = errno = EINVAL;
48250997Sbostic 		return (ERROR);
48350997Sbostic 	}
48450997Sbostic 	hashp = (HTAB *)dbp->internal;
48550997Sbostic 	if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
48650997Sbostic 		hashp->errno = errno = EPERM;
48750997Sbostic 		return (ERROR);
48850997Sbostic 	}
48951057Sbostic 	return (hash_access(flag == R_NOOVERWRITE ?
49050997Sbostic 	    HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data));
49146366Sbostic }
49246366Sbostic 
49346366Sbostic static int
49450997Sbostic hash_delete(dbp, key, flag)
49550997Sbostic 	const DB *dbp;
49650997Sbostic 	const DBT *key;
49750997Sbostic 	u_int flag;		/* Ignored */
49846366Sbostic {
49950997Sbostic 	if (flag && flag != R_CURSOR) {
50050997Sbostic 		hashp->errno = errno = EINVAL;
50150997Sbostic 		return (ERROR);
50250997Sbostic 	}
50350997Sbostic 	hashp = (HTAB *)dbp->internal;
50450997Sbostic 	if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
50550997Sbostic 		hashp->errno = errno = EPERM;
50650997Sbostic 		return (ERROR);
50750997Sbostic 	}
50850997Sbostic 	return (hash_access(HASH_DELETE, (DBT *)key, NULL));
50946366Sbostic }
51046366Sbostic 
51146366Sbostic /*
51250997Sbostic  * Assume that hashp has been set in wrapper routine.
51350997Sbostic  */
51446366Sbostic static int
51546366Sbostic hash_access(action, key, val)
51650997Sbostic 	ACTION action;
51750997Sbostic 	DBT *key, *val;
51846366Sbostic {
51950997Sbostic 	register BUFHEAD *rbufp;
52050997Sbostic 	BUFHEAD *bufp, *save_bufp;
52150997Sbostic 	register u_short *bp;
52250997Sbostic 	register int n, ndx, off, size;
52350997Sbostic 	register char *kp;
52450997Sbostic 	u_short pageno;
52546366Sbostic 
52646366Sbostic #ifdef HASH_STATISTICS
52746366Sbostic 	hash_accesses++;
52846366Sbostic #endif
52946366Sbostic 
53050997Sbostic 	off = hashp->BSIZE;
53146366Sbostic 	size = key->size;
53246950Sbostic 	kp = (char *)key->data;
53350997Sbostic 	rbufp = __get_buf(__call_hash(kp, size), NULL, 0);
53450997Sbostic 	if (!rbufp)
53550997Sbostic 		return (ERROR);
53646457Sbostic 	save_bufp = rbufp;
53746366Sbostic 
53846457Sbostic 	/* Pin the bucket chain */
53946457Sbostic 	rbufp->flags |= BUF_PIN;
54050997Sbostic 	for (bp = (u_short *)rbufp->page, n = *bp++, ndx = 1; ndx < n;)
54150997Sbostic 		if (bp[1] >= REAL_KEY) {
54250997Sbostic 			/* Real key/data pair */
54350997Sbostic 			if (size == off - *bp &&
54450997Sbostic 			    bcmp(kp, rbufp->page + *bp, size) == 0)
54550997Sbostic 				goto found;
54650997Sbostic 			off = bp[1];
54746366Sbostic #ifdef HASH_STATISTICS
54850997Sbostic 			hash_collisions++;
54946366Sbostic #endif
55050997Sbostic 			bp += 2;
55150997Sbostic 			ndx += 2;
55250997Sbostic 		} else if (bp[1] == OVFLPAGE) {
55350997Sbostic 			rbufp = __get_buf(*bp, rbufp, 0);
55450997Sbostic 			if (!rbufp) {
55550997Sbostic 				save_bufp->flags &= ~BUF_PIN;
55650997Sbostic 				return (ERROR);
55750997Sbostic 			}
55850997Sbostic 			/* FOR LOOP INIT */
55950997Sbostic 			bp = (u_short *)rbufp->page;
56050997Sbostic 			n = *bp++;
56150997Sbostic 			ndx = 1;
56250997Sbostic 			off = hashp->BSIZE;
56350997Sbostic 		} else if (bp[1] < REAL_KEY) {
56450997Sbostic 			if ((ndx = __find_bigpair(rbufp, ndx, kp, size)) > 0)
56550997Sbostic 				goto found;
56650997Sbostic 			if (ndx == -2) {
56750997Sbostic 				bufp = rbufp;
56850997Sbostic 				if (!(pageno = __find_last_page(&bufp))) {
56950997Sbostic 					ndx = 0;
57050997Sbostic 					rbufp = bufp;
57150997Sbostic 					break;	/* FOR */
57250997Sbostic 				}
57350997Sbostic 				rbufp = __get_buf(pageno, bufp, 0);
57450997Sbostic 				if (!rbufp) {
57550997Sbostic 					save_bufp->flags &= ~BUF_PIN;
57650997Sbostic 					return (ERROR);
57750997Sbostic 				}
57850997Sbostic 				/* FOR LOOP INIT */
57950997Sbostic 				bp = (u_short *)rbufp->page;
58050997Sbostic 				n = *bp++;
58150997Sbostic 				ndx = 1;
58250997Sbostic 				off = hashp->BSIZE;
58350997Sbostic 			} else {
58450997Sbostic 				save_bufp->flags &= ~BUF_PIN;
58550997Sbostic 				return (ERROR);
58650997Sbostic 			}
58746457Sbostic 		}
58846366Sbostic 
58946366Sbostic 	/* Not found */
59050997Sbostic 	switch (action) {
59150997Sbostic 	case HASH_PUT:
59250997Sbostic 	case HASH_PUTNEW:
59346366Sbostic 		if (__addel(rbufp, key, val)) {
59450997Sbostic 			save_bufp->flags &= ~BUF_PIN;
59550997Sbostic 			return (ERROR);
59646366Sbostic 		} else {
59750997Sbostic 			save_bufp->flags &= ~BUF_PIN;
59850997Sbostic 			return (SUCCESS);
59946366Sbostic 		}
60050997Sbostic 	case HASH_GET:
60150997Sbostic 	case HASH_DELETE:
60250997Sbostic 	default:
60346457Sbostic 		save_bufp->flags &= ~BUF_PIN;
60450997Sbostic 		return (ABNORMAL);
60546366Sbostic 	}
60646366Sbostic 
60746366Sbostic found:
60846366Sbostic 	switch (action) {
60950997Sbostic 	case HASH_PUTNEW:
61046457Sbostic 		save_bufp->flags &= ~BUF_PIN;
61150997Sbostic 		return (ABNORMAL);
61250997Sbostic 	case HASH_GET:
61346366Sbostic 		bp = (u_short *)rbufp->page;
61451072Sbostic 		if (bp[ndx + 1] < REAL_KEY) {
61551057Sbostic 			if (__big_return(rbufp, ndx, val, 0))
61651057Sbostic 				return (ERROR);
61751072Sbostic 		} else {
61850997Sbostic 			val->data = (u_char *)rbufp->page + (int)bp[ndx + 1];
61950997Sbostic 			val->size = bp[ndx] - bp[ndx + 1];
62046366Sbostic 		}
62146366Sbostic 		break;
62250997Sbostic 	case HASH_PUT:
62350997Sbostic 		if ((__delpair(rbufp, ndx)) || (__addel(rbufp, key, val))) {
62450997Sbostic 			save_bufp->flags &= ~BUF_PIN;
62550997Sbostic 			return (ERROR);
62646457Sbostic 		}
62746366Sbostic 		break;
62850997Sbostic 	case HASH_DELETE:
62950997Sbostic 		if (__delpair(rbufp, ndx))
63050997Sbostic 			return (ERROR);
63146366Sbostic 		break;
63246366Sbostic 	}
63346457Sbostic 	save_bufp->flags &= ~BUF_PIN;
63446366Sbostic 	return (SUCCESS);
63546366Sbostic }
63646366Sbostic 
63746366Sbostic static int
63846366Sbostic hash_seq(dbp, key, data, flag)
63950997Sbostic 	const DB *dbp;
64050997Sbostic 	DBT *key, *data;
64150997Sbostic 	u_int flag;
64246366Sbostic {
64350997Sbostic 	register u_int bucket;
64450997Sbostic 	register BUFHEAD *bufp;
64550997Sbostic 	u_short *bp, ndx;
64646366Sbostic 
64750997Sbostic 	if (flag && flag != R_FIRST && flag != R_NEXT) {
64850997Sbostic 		hashp->errno = errno = EINVAL;
64950997Sbostic 		return (ERROR);
65046366Sbostic 	}
65146366Sbostic 	hashp = (HTAB *)dbp->internal;
65246366Sbostic #ifdef HASH_STATISTICS
65346366Sbostic 	hash_accesses++;
65446366Sbostic #endif
65550997Sbostic 	if ((hashp->cbucket < 0) || (flag == R_FIRST)) {
65650997Sbostic 		hashp->cbucket = 0;
65750997Sbostic 		hashp->cndx = 1;
65850997Sbostic 		hashp->cpage = NULL;
65946366Sbostic 	}
66046366Sbostic 
66155874Sbostic 	for (bp = NULL; !bp || !bp[0]; ) {
66255452Sbostic 		if (!(bufp = hashp->cpage)) {
66355452Sbostic 			for (bucket = hashp->cbucket;
66455452Sbostic 			    bucket <= hashp->MAX_BUCKET;
66555452Sbostic 			    bucket++, hashp->cndx = 1) {
66655452Sbostic 				bufp = __get_buf(bucket, NULL, 0);
66755452Sbostic 				if (!bufp)
66855452Sbostic 					return (ERROR);
66955452Sbostic 				hashp->cpage = bufp;
67055452Sbostic 				bp = (u_short *)bufp->page;
67155452Sbostic 				if (bp[0])
67255452Sbostic 					break;
67355452Sbostic 			}
67455452Sbostic 			hashp->cbucket = bucket;
67555452Sbostic 			if (hashp->cbucket > hashp->MAX_BUCKET) {
67655452Sbostic 				hashp->cbucket = -1;
67755452Sbostic 				return (ABNORMAL);
67855452Sbostic 			}
67955452Sbostic 		} else
68055452Sbostic 			bp = (u_short *)hashp->cpage->page;
68155452Sbostic 
68255452Sbostic #ifdef DEBUG
68355452Sbostic 		assert(bp);
68455452Sbostic 		assert(bufp);
68555452Sbostic #endif
68655452Sbostic 		while (bp[hashp->cndx + 1] == OVFLPAGE) {
68755452Sbostic 			bufp = hashp->cpage =
68855452Sbostic 			    __get_buf(bp[hashp->cndx], bufp, 0);
68950997Sbostic 			if (!bufp)
69050997Sbostic 				return (ERROR);
69155452Sbostic 			bp = (u_short *)(bufp->page);
69255452Sbostic 			hashp->cndx = 1;
69350997Sbostic 		}
69455874Sbostic 		if (!bp[0]) {
69555452Sbostic 			hashp->cpage = NULL;
69655874Sbostic 			++hashp->cbucket;
69755874Sbostic 		}
69846366Sbostic 	}
69946366Sbostic 	ndx = hashp->cndx;
70050997Sbostic 	if (bp[ndx + 1] < REAL_KEY) {
70151057Sbostic 		if (__big_keydata(bufp, key, data, 1))
70250997Sbostic 			return (ERROR);
70346366Sbostic 	} else {
70450997Sbostic 		key->data = (u_char *)hashp->cpage->page + bp[ndx];
70550997Sbostic 		key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx];
70650997Sbostic 		data->data = (u_char *)hashp->cpage->page + bp[ndx + 1];
70750997Sbostic 		data->size = bp[ndx] - bp[ndx + 1];
70850997Sbostic 		ndx += 2;
70950997Sbostic 		if (ndx > bp[0]) {
71050997Sbostic 			hashp->cpage = NULL;
71150997Sbostic 			hashp->cbucket++;
71250997Sbostic 			hashp->cndx = 1;
71350997Sbostic 		} else
71450997Sbostic 			hashp->cndx = ndx;
71546366Sbostic 	}
71646366Sbostic 	return (SUCCESS);
71746366Sbostic }
71846366Sbostic 
71946366Sbostic /********************************* UTILITIES ************************/
72050997Sbostic 
72146366Sbostic /*
72250997Sbostic  * Returns:
72350997Sbostic  *	 0 ==> OK
72450997Sbostic  *	-1 ==> Error
72550997Sbostic  */
72646366Sbostic extern int
72746366Sbostic __expand_table()
72846366Sbostic {
72950997Sbostic 	u_int old_bucket, new_bucket;
73050997Sbostic 	int dirsize, new_segnum, spare_ndx;
73150997Sbostic 
73246366Sbostic #ifdef HASH_STATISTICS
73346366Sbostic 	hash_expansions++;
73446366Sbostic #endif
73546366Sbostic 	new_bucket = ++hashp->MAX_BUCKET;
73646366Sbostic 	old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK);
73746366Sbostic 
73846366Sbostic 	new_segnum = new_bucket >> hashp->SSHIFT;
73946366Sbostic 
74046366Sbostic 	/* Check if we need a new segment */
74150997Sbostic 	if (new_segnum >= hashp->nsegs) {
74250997Sbostic 		/* Check if we need to expand directory */
74350997Sbostic 		if (new_segnum >= hashp->DSIZE) {
74450997Sbostic 			/* Reallocate directory */
74550997Sbostic 			dirsize = hashp->DSIZE * sizeof(SEGMENT *);
74650997Sbostic 			if (!hash_realloc(&hashp->dir, dirsize, dirsize << 1))
74750997Sbostic 				return (-1);
74850997Sbostic 			hashp->DSIZE = dirsize << 1;
74946366Sbostic 		}
75050997Sbostic 		if (!(hashp->dir[new_segnum] =
75150997Sbostic 			calloc(hashp->SGSIZE, sizeof(SEGMENT))))
75250997Sbostic 			return (-1);
75350997Sbostic 		hashp->exsegs++;
75450997Sbostic 		hashp->nsegs++;
75546366Sbostic 	}
75646366Sbostic 	/*
75750997Sbostic 	 * If the split point is increasing (MAX_BUCKET's log base 2
75850997Sbostic 	 * * increases), we need to copy the current contents of the spare
75950997Sbostic 	 * split bucket to the next bucket.
76050997Sbostic 	 */
76152001Sbostic 	spare_ndx = __log2(hashp->MAX_BUCKET + 1);
76252001Sbostic 	if (spare_ndx > hashp->OVFL_POINT) {
76351061Sbostic 		hashp->SPARES[spare_ndx] = hashp->SPARES[hashp->OVFL_POINT];
76451061Sbostic 		hashp->OVFL_POINT = spare_ndx;
76551061Sbostic 	}
76651061Sbostic 
76750997Sbostic 	if (new_bucket > hashp->HIGH_MASK) {
76850997Sbostic 		/* Starting a new doubling */
76950997Sbostic 		hashp->LOW_MASK = hashp->HIGH_MASK;
77050997Sbostic 		hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK;
77146366Sbostic 	}
77250997Sbostic 	/* Relocate records to the new bucket */
77350997Sbostic 	return (__split_page(old_bucket, new_bucket));
77450997Sbostic }
77546366Sbostic 
77646366Sbostic /*
77750997Sbostic  * If realloc guarantees that the pointer is not destroyed if the realloc
77850997Sbostic  * fails, then this routine can go away.
77950997Sbostic  */
78050997Sbostic static void *
78150997Sbostic hash_realloc(p_ptr, oldsize, newsize)
78250997Sbostic 	SEGMENT **p_ptr;
78350997Sbostic 	int oldsize, newsize;
78446366Sbostic {
78550997Sbostic 	register void *p;
78646366Sbostic 
78750997Sbostic 	if (p = malloc(newsize)) {
78850997Sbostic 		bcopy(*p_ptr, p, oldsize);
78950997Sbostic 		bzero(*p_ptr + oldsize, newsize - oldsize);
79046366Sbostic 		free(*p_ptr);
79146366Sbostic 		*p_ptr = p;
79246366Sbostic 	}
79346366Sbostic 	return (p);
79446366Sbostic }
79546366Sbostic 
79647251Sbostic extern u_int
79750997Sbostic __call_hash(k, len)
79850997Sbostic 	char *k;
79950997Sbostic 	int len;
80046366Sbostic {
80150997Sbostic 	int n, bucket;
80250997Sbostic 
80346366Sbostic 	n = hashp->hash(k, len);
80446366Sbostic 	bucket = n & hashp->HIGH_MASK;
80550997Sbostic 	if (bucket > hashp->MAX_BUCKET)
80650997Sbostic 		bucket = bucket & hashp->LOW_MASK;
80750997Sbostic 	return (bucket);
80846366Sbostic }
80946366Sbostic 
81046366Sbostic /*
81150997Sbostic  * Allocate segment table.  On error, destroy the table and set errno.
81250997Sbostic  *
81350997Sbostic  * Returns 0 on success
81450997Sbostic  */
81546366Sbostic static int
81650997Sbostic alloc_segs(nsegs)
81750997Sbostic 	int nsegs;
81846366Sbostic {
81950997Sbostic 	register int i;
82050997Sbostic 	register SEGMENT store;
82146366Sbostic 
82250997Sbostic 	int save_errno;
82346366Sbostic 
82450997Sbostic 	if (!(hashp->dir = calloc(hashp->DSIZE, sizeof(SEGMENT *)))) {
82550997Sbostic 		save_errno = errno;
82650997Sbostic 		(void)hdestroy();
82750997Sbostic 		errno = save_errno;
82850997Sbostic 		return (-1);
82950997Sbostic 	}
83050997Sbostic 	/* Allocate segments */
83150997Sbostic 	store = calloc(nsegs << hashp->SSHIFT, sizeof(SEGMENT));
83250997Sbostic 	if (!store) {
83350997Sbostic 		save_errno = errno;
83450997Sbostic 		(void)hdestroy();
83550997Sbostic 		errno = save_errno;
83650997Sbostic 		return (-1);
83750997Sbostic 	}
83850997Sbostic 	for (i = 0; i < nsegs; i++, hashp->nsegs++)
83950997Sbostic 		hashp->dir[i] = &store[i << hashp->SSHIFT];
84050997Sbostic 	return (0);
84146366Sbostic }
84246366Sbostic 
84350997Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
84446366Sbostic /*
84550997Sbostic  * Hashp->hdr needs to be byteswapped.
84650997Sbostic  */
84746366Sbostic static void
84850997Sbostic swap_header_copy(srcp, destp)
84950997Sbostic 	HASHHDR *srcp, *destp;
85046366Sbostic {
85150997Sbostic 	int i;
85246366Sbostic 
85350997Sbostic 	BLSWAP_COPY(srcp->magic, destp->magic);
85450997Sbostic 	BLSWAP_COPY(srcp->version, destp->version);
85550997Sbostic 	BLSWAP_COPY(srcp->lorder, destp->lorder);
85650997Sbostic 	BLSWAP_COPY(srcp->bsize, destp->bsize);
85750997Sbostic 	BLSWAP_COPY(srcp->bshift, destp->bshift);
85850997Sbostic 	BLSWAP_COPY(srcp->dsize, destp->dsize);
85950997Sbostic 	BLSWAP_COPY(srcp->ssize, destp->ssize);
86050997Sbostic 	BLSWAP_COPY(srcp->sshift, destp->sshift);
86151061Sbostic 	BLSWAP_COPY(srcp->ovfl_point, destp->ovfl_point);
86251061Sbostic 	BLSWAP_COPY(srcp->last_freed, destp->last_freed);
86350997Sbostic 	BLSWAP_COPY(srcp->max_bucket, destp->max_bucket);
86450997Sbostic 	BLSWAP_COPY(srcp->high_mask, destp->high_mask);
86550997Sbostic 	BLSWAP_COPY(srcp->low_mask, destp->low_mask);
86650997Sbostic 	BLSWAP_COPY(srcp->ffactor, destp->ffactor);
86750997Sbostic 	BLSWAP_COPY(srcp->nkeys, destp->nkeys);
86850997Sbostic 	BLSWAP_COPY(srcp->hdrpages, destp->hdrpages);
86950997Sbostic 	BLSWAP_COPY(srcp->h_charkey, destp->h_charkey);
87050997Sbostic 	for (i = 0; i < NCACHED; i++) {
87150997Sbostic 		BLSWAP_COPY(srcp->spares[i], destp->spares[i]);
87250997Sbostic 		BSSWAP_COPY(srcp->bitmaps[i], destp->bitmaps[i]);
87350997Sbostic 	}
87446366Sbostic }
87546366Sbostic 
87646366Sbostic static void
87750997Sbostic swap_header()
87846366Sbostic {
87950997Sbostic 	HASHHDR *hdrp;
88050997Sbostic 	int i;
88146366Sbostic 
88250997Sbostic 	hdrp = &hashp->hdr;
88346366Sbostic 
88450997Sbostic 	BLSWAP(hdrp->magic);
88550997Sbostic 	BLSWAP(hdrp->version);
88650997Sbostic 	BLSWAP(hdrp->lorder);
88750997Sbostic 	BLSWAP(hdrp->bsize);
88850997Sbostic 	BLSWAP(hdrp->bshift);
88950997Sbostic 	BLSWAP(hdrp->dsize);
89050997Sbostic 	BLSWAP(hdrp->ssize);
89150997Sbostic 	BLSWAP(hdrp->sshift);
89251061Sbostic 	BLSWAP(hdrp->ovfl_point);
89351061Sbostic 	BLSWAP(hdrp->last_freed);
89450997Sbostic 	BLSWAP(hdrp->max_bucket);
89550997Sbostic 	BLSWAP(hdrp->high_mask);
89650997Sbostic 	BLSWAP(hdrp->low_mask);
89750997Sbostic 	BLSWAP(hdrp->ffactor);
89850997Sbostic 	BLSWAP(hdrp->nkeys);
89950997Sbostic 	BLSWAP(hdrp->hdrpages);
90050997Sbostic 	BLSWAP(hdrp->h_charkey);
90150997Sbostic 	for (i = 0; i < NCACHED; i++) {
90250997Sbostic 		BLSWAP(hdrp->spares[i]);
90350997Sbostic 		BSSWAP(hdrp->bitmaps[i]);
90450997Sbostic 	}
90546366Sbostic }
90650997Sbostic #endif
907