xref: /csrg-svn/lib/libc/db/hash/hash.c (revision 50997)
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*50997Sbostic static char sccsid[] = "@(#)hash.c	5.15 (Berkeley) 09/04/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>
19*50997Sbostic #ifdef DEBUG
2046366Sbostic #include <assert.h>
21*50997Sbostic #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"
28*50997Sbostic #include "page.h"
29*50997Sbostic #include "extern.h"
3046366Sbostic 
31*50997Sbostic static int   alloc_segs __P((int));
32*50997Sbostic static int   flush_meta __P((void));
33*50997Sbostic static int   hash_access __P((ACTION, DBT *, DBT *));
34*50997Sbostic static int   hash_close __P((DB *));
35*50997Sbostic static int   hash_delete __P((const DB *, const DBT *, u_int));
36*50997Sbostic static int   hash_get __P((const DB *, DBT *, DBT *, u_int));
37*50997Sbostic static int   hash_put __P((const DB *, const DBT *, const DBT *, u_int));
38*50997Sbostic static void *hash_realloc __P((SEGMENT **, int, int));
39*50997Sbostic static int   hash_seq __P((const DB *, DBT *, DBT *, u_int));
40*50997Sbostic static int   hash_sync __P((const DB *));
41*50997Sbostic static int   hdestroy __P((void));
42*50997Sbostic static HTAB *init_hash __P((HASHINFO *));
43*50997Sbostic static int   init_htab __P((int));
44*50997Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
45*50997Sbostic static void  swap_header __P((void));
46*50997Sbostic static void  swap_header_copy __P((HASHHDR *, HASHHDR *));
47*50997Sbostic #endif
48*50997Sbostic 
4946366Sbostic /* Fast arithmetic, relying on powers of 2, */
50*50997Sbostic #define MOD(x, y)		((x) & ((y) - 1))
5146366Sbostic 
52*50997Sbostic #define RETURN_ERROR(ERR, LOC)	{ save_errno = ERR; goto LOC; }
5346366Sbostic 
5446366Sbostic /* Return values */
55*50997Sbostic #define	SUCCESS	 (0)
56*50997Sbostic #define	ERROR	(-1)
57*50997Sbostic #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 
66*50997Sbostic /************************** INTERFACE ROUTINES ***************************/
6746366Sbostic /* OPEN/CLOSE */
6846366Sbostic 
69*50997Sbostic extern DB *
70*50997Sbostic hash_open(file, flags, mode, info)
71*50997Sbostic 	const char *file;
72*50997Sbostic 	int flags, mode;
73*50997Sbostic 	const HASHINFO *info;	/* Special directives for create */
7446366Sbostic {
75*50997Sbostic 	struct stat statbuf;
76*50997Sbostic 	DB *dbp;
77*50997Sbostic 	int bpages, hdrsize, new_table, nsegs, save_errno;
7846366Sbostic 
79*50997Sbostic 	if (!(hashp = calloc(1, sizeof(HTAB))))
80*50997Sbostic 		return (NULL);
81*50997Sbostic 	hashp->fp = -1;
82*50997Sbostic 	/*
83*50997Sbostic 	 * Select flags relevant to us. Even if user wants write only, we need
84*50997Sbostic 	 * to be able to read the actual file, so we need to open it read/write.
85*50997Sbostic 	 * But, the field in the hashp structure needs to be accurate so that
86*50997Sbostic 	 * we can check accesses.
87*50997Sbostic 	 */
88*50997Sbostic 	hashp->flags = flags =
89*50997Sbostic 	    flags & (O_CREAT | O_EXCL | O_RDONLY | O_RDWR | O_TRUNC | O_WRONLY);
90*50997Sbostic 	if (flags & O_WRONLY)
91*50997Sbostic 		flags = (flags & ~O_WRONLY) | O_RDWR;
9246366Sbostic 
93*50997Sbostic 	new_table = 0;
94*50997Sbostic 	if (!file || (flags & O_TRUNC) ||
95*50997Sbostic 	    (stat(file, &statbuf) && (errno == ENOENT))) {
96*50997Sbostic 		if (errno == ENOENT)
97*50997Sbostic 			errno = 0; /* Just in case someone looks at errno */
98*50997Sbostic 		new_table = 1;
9946485Sbostic 	}
100*50997Sbostic 	if (file) {
101*50997Sbostic 		if ((hashp->fp = open(file, flags, mode)) == -1)
102*50997Sbostic 			RETURN_ERROR(errno, error0);
103*50997Sbostic 		(void)fcntl(hashp->fp, F_SETFD, 1);
10446366Sbostic 	}
105*50997Sbostic 	if (new_table) {
106*50997Sbostic 		if (!(hashp = init_hash((HASHINFO *)info)))
107*50997Sbostic 			RETURN_ERROR(errno, error1);
108*50997Sbostic 	} else {
109*50997Sbostic 		/* Table already exists */
110*50997Sbostic 		if (info && info->hash)
111*50997Sbostic 			hashp->hash = info->hash;
112*50997Sbostic 		else
113*50997Sbostic 			hashp->hash = default_hash;
11446366Sbostic 
115*50997Sbostic 		hdrsize = read(hashp->fp, &hashp->hdr, sizeof(HASHHDR));
11646366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
117*50997Sbostic 		swap_header();
11846366Sbostic #endif
119*50997Sbostic 		if (hdrsize == -1)
120*50997Sbostic 			RETURN_ERROR(errno, error1);
121*50997Sbostic 		if (hdrsize != sizeof(HASHHDR))
122*50997Sbostic 			RETURN_ERROR(EFTYPE, error1);
123*50997Sbostic 		/* Verify file type, versions and hash function */
124*50997Sbostic 		if (hashp->MAGIC != HASHMAGIC)
125*50997Sbostic 			RETURN_ERROR(EFTYPE, error1);
126*50997Sbostic 		if (hashp->VERSION != VERSION_NO)
127*50997Sbostic 			RETURN_ERROR(EFTYPE, error1);
128*50997Sbostic 		if (hashp->hash(CHARKEY, sizeof(CHARKEY)) != hashp->H_CHARKEY)
129*50997Sbostic 			RETURN_ERROR(EFTYPE, error1);
130*50997Sbostic 		/*
131*50997Sbostic 			Figure out how many segments we need.
132*50997Sbostic 			Max_Bucket it the maximum bucket number, so the
133*50997Sbostic 			number of buckets is max_bucket+1.
134*50997Sbostic 		*/
135*50997Sbostic 		nsegs = (hashp->MAX_BUCKET + 1 + hashp->SGSIZE - 1) /
136*50997Sbostic 			 hashp->SGSIZE;
137*50997Sbostic 		hashp->nsegs = 0;
138*50997Sbostic 		if (alloc_segs(nsegs))
139*50997Sbostic 			/*
140*50997Sbostic 			 * If alloc_segs fails, table will have been destroyed
141*50997Sbostic 			 * and errno will have been set.
142*50997Sbostic 			 */
143*50997Sbostic 			return (NULL);
144*50997Sbostic 		/* Read in bitmaps */
145*50997Sbostic 		bpages = (hashp->SPARES[__log2(hashp->MAX_BUCKET)] +
146*50997Sbostic 		    (hashp->BSIZE << BYTE_SHIFT) - 1) >>
147*50997Sbostic 		    (hashp->BSHIFT + BYTE_SHIFT);
14846366Sbostic 
149*50997Sbostic 		hashp->nmaps = bpages;
150*50997Sbostic 		memset(&hashp->mapp[0], 0, bpages * sizeof(u_long *));
15146366Sbostic 	}
15246366Sbostic 
153*50997Sbostic 	/* Initialize Buffer Manager */
154*50997Sbostic 	if (info && info->cachesize)
155*50997Sbostic 		__buf_init(info->cachesize);
156*50997Sbostic 	else
157*50997Sbostic 		__buf_init(DEF_BUFSIZE);
158*50997Sbostic 
159*50997Sbostic 	hashp->new_file = new_table;
160*50997Sbostic 	hashp->save_file = file && (hashp->flags & (O_WRONLY | O_RDWR));
161*50997Sbostic 	hashp->cbucket = -1;
162*50997Sbostic 	if (!(dbp = malloc(sizeof(DB)))) {
163*50997Sbostic 		save_errno = errno;
164*50997Sbostic 		hdestroy();
165*50997Sbostic 		errno = save_errno;
166*50997Sbostic 		return (NULL);
16746366Sbostic 	}
168*50997Sbostic 	dbp->internal = (char *)hashp;
169*50997Sbostic 	dbp->close = hash_close;
170*50997Sbostic 	dbp->del = hash_delete;
171*50997Sbostic 	dbp->get = hash_get;
172*50997Sbostic 	dbp->put = hash_put;
173*50997Sbostic 	dbp->seq = hash_seq;
174*50997Sbostic 	dbp->sync = hash_sync;
175*50997Sbostic 	dbp->type = DB_HASH;
17646366Sbostic 
17746366Sbostic #ifdef DEBUG
178*50997Sbostic 	(void)fprintf(stderr,
179*50997Sbostic "%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",
180*50997Sbostic 	    "init_htab:",
181*50997Sbostic 	    "TABLE POINTER   ", hashp,
182*50997Sbostic 	    "BUCKET SIZE     ", hashp->BSIZE,
183*50997Sbostic 	    "BUCKET SHIFT    ", hashp->BSHIFT,
184*50997Sbostic 	    "DIRECTORY SIZE  ", hashp->DSIZE,
185*50997Sbostic 	    "SEGMENT SIZE    ", hashp->SGSIZE,
186*50997Sbostic 	    "SEGMENT SHIFT   ", hashp->SSHIFT,
187*50997Sbostic 	    "FILL FACTOR     ", hashp->FFACTOR,
188*50997Sbostic 	    "MAX BUCKET      ", hashp->MAX_BUCKET,
189*50997Sbostic 	    "HIGH MASK       ", hashp->HIGH_MASK,
190*50997Sbostic 	    "LOW  MASK       ", hashp->LOW_MASK,
191*50997Sbostic 	    "NSEGS           ", hashp->nsegs,
192*50997Sbostic 	    "NKEYS           ", hashp->NKEYS);
19346366Sbostic #endif
19446366Sbostic #ifdef HASH_STATISTICS
19546366Sbostic 	hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0;
19646366Sbostic #endif
197*50997Sbostic 	return (dbp);
19846366Sbostic 
19946366Sbostic error1:
200*50997Sbostic 	(void)close(hashp->fp);
20146366Sbostic 
20246366Sbostic error0:
203*50997Sbostic 	free(hashp);
204*50997Sbostic 	errno = save_errno;
205*50997Sbostic 	return (NULL);
20646366Sbostic }
20746366Sbostic 
20846366Sbostic static int
209*50997Sbostic hash_close(dbp)
210*50997Sbostic 	DB *dbp;
21146366Sbostic {
212*50997Sbostic 	int retval;
21346457Sbostic 
214*50997Sbostic 	if (!dbp)
215*50997Sbostic 		return (ERROR);
21646366Sbostic 	hashp = (HTAB *)dbp->internal;
21746457Sbostic 	retval = hdestroy();
218*50997Sbostic 	free(dbp);
219*50997Sbostic 	return (retval);
22046366Sbostic }
22146366Sbostic 
22246366Sbostic /************************** LOCAL CREATION ROUTINES **********************/
223*50997Sbostic static HTAB *
224*50997Sbostic init_hash(info)
225*50997Sbostic 	HASHINFO *info;
22646366Sbostic {
227*50997Sbostic 	int nelem;
22846366Sbostic 
22946366Sbostic 	nelem = 1;
230*50997Sbostic 	hashp->LORDER = BYTE_ORDER;
231*50997Sbostic 	hashp->BSIZE = DEF_BUCKET_SIZE;
232*50997Sbostic 	hashp->BSHIFT = DEF_BUCKET_SHIFT;
233*50997Sbostic 	hashp->SGSIZE = DEF_SEGSIZE;
234*50997Sbostic 	hashp->SSHIFT = DEF_SEGSIZE_SHIFT;
235*50997Sbostic 	hashp->DSIZE = DEF_DIRSIZE;
236*50997Sbostic 	hashp->FFACTOR = DEF_FFACTOR;
237*50997Sbostic 	hashp->hash = default_hash;
238*50997Sbostic 	bzero(hashp->SPARES, sizeof(hashp->SPARES));
23946366Sbostic 
240*50997Sbostic 	if (info) {
241*50997Sbostic 		if (info->bsize) {
242*50997Sbostic 			/* Round pagesize up to power of 2 */
243*50997Sbostic 			hashp->BSHIFT = __log2(info->bsize);
244*50997Sbostic 			hashp->BSIZE = 1 << hashp->BSHIFT;
245*50997Sbostic 			if (hashp->BSIZE > MAX_BSIZE) {
246*50997Sbostic 				errno = EINVAL;
247*50997Sbostic 				return (NULL);
248*50997Sbostic 			}
24946366Sbostic 		}
250*50997Sbostic 		if (info->ffactor)
251*50997Sbostic 			hashp->FFACTOR = info->ffactor;
252*50997Sbostic 		if (info->hash)
253*50997Sbostic 			hashp->hash = info->hash;
254*50997Sbostic 		if (info->nelem)
255*50997Sbostic 			nelem = info->nelem;
256*50997Sbostic 		if (info->lorder) {
257*50997Sbostic 			if (info->lorder != BIG_ENDIAN &&
258*50997Sbostic 			    info->lorder != LITTLE_ENDIAN) {
259*50997Sbostic 				errno = EINVAL;
260*50997Sbostic 				return (NULL);
261*50997Sbostic 			}
262*50997Sbostic 			hashp->LORDER = info->lorder;
26346366Sbostic 		}
26446366Sbostic 	}
26546366Sbostic 	/* init_htab should destroy the table and set errno if it fails */
266*50997Sbostic 	if (init_htab(nelem))
267*50997Sbostic 		return (NULL);
268*50997Sbostic 	else
269*50997Sbostic 		return (hashp);
27046366Sbostic }
27146366Sbostic /*
272*50997Sbostic  * This calls alloc_segs which may run out of memory.  Alloc_segs will destroy
273*50997Sbostic  * the table and set errno, so we just pass the error information along.
274*50997Sbostic  *
275*50997Sbostic  * Returns 0 on No Error
276*50997Sbostic  */
27746366Sbostic static int
278*50997Sbostic init_htab(nelem)
279*50997Sbostic 	int nelem;
28046366Sbostic {
281*50997Sbostic 	register int nbuckets, nsegs;
282*50997Sbostic 	int l2;
28346366Sbostic 
284*50997Sbostic 	/*
285*50997Sbostic 	 * Divide number of elements by the fill factor and determine a
286*50997Sbostic 	 * desired number of buckets.  Allocate space for the next greater
287*50997Sbostic 	 * power of two number of buckets.
288*50997Sbostic 	 */
28946366Sbostic 	nelem = (nelem - 1) / hashp->FFACTOR + 1;
29046366Sbostic 
29146366Sbostic 	l2 = __log2(nelem);
29246366Sbostic 	nbuckets = 1 << l2;
293*50997Sbostic 	nbuckets = MAX(nbuckets, 2);
29446366Sbostic 
29546366Sbostic 	hashp->SPARES[l2] = l2 + 1;
296*50997Sbostic 	hashp->SPARES[l2 + 1] = l2 + 1;
297*50997Sbostic 	/* First bitmap page is at: splitpoint l2 page offset 1 */
298*50997Sbostic 	__init_bitmap(OADDR_OF(l2, 1), l2 + 1, 0);
29946366Sbostic 
30046366Sbostic 	hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1;
30146366Sbostic 	hashp->HIGH_MASK = (nbuckets << 1) - 1;
302*50997Sbostic 	hashp->HDRPAGES = ((MAX(sizeof(HASHHDR), MINHDRSIZE) - 1) >>
303*50997Sbostic 	    hashp->BSHIFT) + 1;
30446366Sbostic 
30546366Sbostic 	nsegs = (nbuckets - 1) / hashp->SGSIZE + 1;
30646366Sbostic 	nsegs = 1 << __log2(nsegs);
30746366Sbostic 
308*50997Sbostic 	if (nsegs > hashp->DSIZE)
309*50997Sbostic 		hashp->DSIZE = nsegs;
310*50997Sbostic 	return (alloc_segs(nsegs));
31146366Sbostic }
31246366Sbostic 
31346366Sbostic /********************** DESTROY/CLOSE ROUTINES ************************/
31446366Sbostic 
31546366Sbostic /*
316*50997Sbostic  * Flushes any changes to the file if necessary and destroys the hashp
317*50997Sbostic  * structure, freeing all allocated space.
318*50997Sbostic  */
31946366Sbostic static int
32046366Sbostic hdestroy()
32146366Sbostic {
322*50997Sbostic 	int i, save_errno;
32346366Sbostic 
32446366Sbostic 	save_errno = 0;
32546366Sbostic 
32646366Sbostic 	if (hashp != NULL) {
32746366Sbostic #ifdef HASH_STATISTICS
328*50997Sbostic 		(void)fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n",
329*50997Sbostic 		    hash_accesses, hash_collisions);
330*50997Sbostic 		(void)fprintf(stderr, "hdestroy: expansions %ld\n",
331*50997Sbostic 		    hash_expansions);
332*50997Sbostic 		(void)fprintf(stderr, "hdestroy: overflows %ld\n",
333*50997Sbostic 		    hash_overflows);
334*50997Sbostic 		(void)fprintf(stderr, "keys %ld maxp %d segmentcount %d\n",
335*50997Sbostic 		    hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs);
33646366Sbostic 
337*50997Sbostic 		for (i = 0; i < NCACHED; i++)
338*50997Sbostic 			(void)fprintf(stderr,
339*50997Sbostic 			    "spares[%d] = %d\n", i, hashp->SPARES[i]);
34046366Sbostic #endif
341*50997Sbostic 		/*
342*50997Sbostic 		 * Call on buffer manager to free buffers, and if required,
343*50997Sbostic 		 * write them to disk.
344*50997Sbostic 		 */
345*50997Sbostic 		if (__buf_free(1, hashp->save_file))
346*50997Sbostic 			save_errno = errno;
347*50997Sbostic 		if (hashp->dir) {
348*50997Sbostic 			free(*hashp->dir);	/* Free initial segments */
349*50997Sbostic 			/* Free extra segments */
350*50997Sbostic 			while (hashp->exsegs--)
351*50997Sbostic 				free(hashp->dir[--hashp->nsegs]);
352*50997Sbostic 			free(hashp->dir);
35346366Sbostic 		}
354*50997Sbostic 		if (flush_meta() && !save_errno)
355*50997Sbostic 			save_errno = errno;
35647251Sbostic 		/* Free Bigmaps */
357*50997Sbostic 		for (i = 0; i < hashp->nmaps; i++)
358*50997Sbostic 			if (hashp->mapp[i])
359*50997Sbostic 				free(hashp->mapp[i]);
36046503Sbostic 
361*50997Sbostic 		if (hashp->fp != -1)
362*50997Sbostic 			(void)close(hashp->fp);
363*50997Sbostic 		free(hashp);
36446366Sbostic 		hashp = NULL;
36546366Sbostic 	}
366*50997Sbostic 	if (save_errno) {
367*50997Sbostic 		errno = save_errno;
368*50997Sbostic 		return (ERROR);
36946366Sbostic 	}
370*50997Sbostic 	return (SUCCESS);
37146366Sbostic }
372*50997Sbostic /*
373*50997Sbostic  * Write modified pages to disk
374*50997Sbostic  *
375*50997Sbostic  * Returns:
376*50997Sbostic  *	 0 == OK
377*50997Sbostic  *	-1 ERROR
378*50997Sbostic  */
37946366Sbostic static int
38046366Sbostic hash_sync(dbp)
381*50997Sbostic 	const DB *dbp;
38246366Sbostic {
383*50997Sbostic 	if (!dbp)
384*50997Sbostic 		return (ERROR);
38546366Sbostic 	hashp = (HTAB *)dbp->internal;
38646366Sbostic 
387*50997Sbostic 	if (!hashp->save_file)
388*50997Sbostic 		return (0);
389*50997Sbostic 	if (__buf_free(0, 1) || flush_meta())
390*50997Sbostic 		return (ERROR);
39146366Sbostic 	hashp->new_file = 0;
392*50997Sbostic 	return (0);
39346366Sbostic }
39446366Sbostic 
39546366Sbostic /*
396*50997Sbostic  * Returns:
397*50997Sbostic  *	 0 == OK
398*50997Sbostic  *	-1 indicates that errno should be set
399*50997Sbostic  */
40046366Sbostic static int
40146366Sbostic flush_meta()
40246366Sbostic {
403*50997Sbostic 	HASHHDR *whdrp;
404*50997Sbostic 	int fp, i, wsize;
40546366Sbostic 
406*50997Sbostic 	if (!hashp->save_file)
407*50997Sbostic 		return (0);
40846366Sbostic 	hashp->MAGIC = HASHMAGIC;
40946366Sbostic 	hashp->VERSION = VERSION_NO;
410*50997Sbostic 	hashp->H_CHARKEY = hashp->hash(CHARKEY, sizeof(CHARKEY));
41146366Sbostic 
41246366Sbostic 	fp = hashp->fp;
41346366Sbostic 	whdrp = &hashp->hdr;
41446366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
41546366Sbostic 	whdrp = &whdr;
416*50997Sbostic 	swap_header_copy(&hashp->hdr, whdrp);
41746366Sbostic #endif
418*50997Sbostic 	if ((lseek(fp, 0, SEEK_SET) == -1) ||
419*50997Sbostic 	    ((wsize = write(fp, whdrp, sizeof(HASHHDR))) == -1))
420*50997Sbostic 		return (-1);
421*50997Sbostic 	else
422*50997Sbostic 		if (wsize != sizeof(HASHHDR)) {
423*50997Sbostic 			errno = EFTYPE;
424*50997Sbostic 			hashp->errno = errno;
425*50997Sbostic 			return (-1);
42646366Sbostic 		}
427*50997Sbostic 	for (i = 0; i < NCACHED; i++)
428*50997Sbostic 		if (hashp->mapp[i])
429*50997Sbostic 			if (!__put_page((char *)hashp->mapp[i],
430*50997Sbostic 				hashp->BITMAPS[i], 0, 1))
431*50997Sbostic 				return (-1);
432*50997Sbostic 	return (0);
43346366Sbostic }
434*50997Sbostic 
43546366Sbostic /*******************************SEARCH ROUTINES *****************************/
43646366Sbostic /*
437*50997Sbostic  * All the access routines return
438*50997Sbostic  *
439*50997Sbostic  * Returns:
440*50997Sbostic  *	 0 on SUCCESS
441*50997Sbostic  *	 1 to indicate an external ERROR (i.e. key not found, etc)
442*50997Sbostic  *	-1 to indicate an internal ERROR (i.e. out of memory, etc)
443*50997Sbostic  */
44446366Sbostic static int
445*50997Sbostic hash_get(dbp, key, data, flag)
446*50997Sbostic 	const DB *dbp;
447*50997Sbostic 	DBT *key, *data;
448*50997Sbostic 	u_int flag;
44946366Sbostic {
450*50997Sbostic 	if (flag) {
451*50997Sbostic 		hashp->errno = errno = EINVAL;
452*50997Sbostic 		return (ERROR);
453*50997Sbostic 	}
454*50997Sbostic 	hashp = (HTAB *)dbp->internal;
455*50997Sbostic 	if (hashp->flags & O_WRONLY) {
456*50997Sbostic 		hashp->errno = errno = EPERM;
457*50997Sbostic 		return (ERROR);
458*50997Sbostic 	}
459*50997Sbostic 	return (hash_access(HASH_GET, key, data));
46046366Sbostic }
46146366Sbostic 
46246366Sbostic static int
463*50997Sbostic hash_put(dbp, key, data, flag)
464*50997Sbostic 	const DB *dbp;
465*50997Sbostic 	const DBT *key, *data;
466*50997Sbostic 	u_int flag;
46746366Sbostic {
468*50997Sbostic 	if (flag && flag != R_NOOVERWRITE) {
469*50997Sbostic 		hashp->errno = errno = EINVAL;
470*50997Sbostic 		return (ERROR);
471*50997Sbostic 	}
472*50997Sbostic 	hashp = (HTAB *)dbp->internal;
473*50997Sbostic 	if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
474*50997Sbostic 		hashp->errno = errno = EPERM;
475*50997Sbostic 		return (ERROR);
476*50997Sbostic 	}
477*50997Sbostic 	return (hash_access(flag == R_NOOVERWRITE ?
478*50997Sbostic 	    HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data));
47946366Sbostic }
48046366Sbostic 
48146366Sbostic static int
482*50997Sbostic hash_delete(dbp, key, flag)
483*50997Sbostic 	const DB *dbp;
484*50997Sbostic 	const DBT *key;
485*50997Sbostic 	u_int flag;		/* Ignored */
48646366Sbostic {
487*50997Sbostic 	if (flag && flag != R_CURSOR) {
488*50997Sbostic 		hashp->errno = errno = EINVAL;
489*50997Sbostic 		return (ERROR);
490*50997Sbostic 	}
491*50997Sbostic 	hashp = (HTAB *)dbp->internal;
492*50997Sbostic 	if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
493*50997Sbostic 		hashp->errno = errno = EPERM;
494*50997Sbostic 		return (ERROR);
495*50997Sbostic 	}
496*50997Sbostic 	return (hash_access(HASH_DELETE, (DBT *)key, NULL));
49746366Sbostic }
49846366Sbostic 
49946366Sbostic /*
500*50997Sbostic  * Assume that hashp has been set in wrapper routine.
501*50997Sbostic  */
50246366Sbostic static int
50346366Sbostic hash_access(action, key, val)
504*50997Sbostic 	ACTION action;
505*50997Sbostic 	DBT *key, *val;
50646366Sbostic {
507*50997Sbostic 	register BUFHEAD *rbufp;
508*50997Sbostic 	BUFHEAD *bufp, *save_bufp;
509*50997Sbostic 	register u_short *bp;
510*50997Sbostic 	register int n, ndx, off, size;
511*50997Sbostic 	register char *kp;
512*50997Sbostic 	u_short pageno;
51346366Sbostic 
51446366Sbostic #ifdef HASH_STATISTICS
51546366Sbostic 	hash_accesses++;
51646366Sbostic #endif
51746366Sbostic 
518*50997Sbostic 	off = hashp->BSIZE;
51946366Sbostic 	size = key->size;
52046950Sbostic 	kp = (char *)key->data;
521*50997Sbostic 	rbufp = __get_buf(__call_hash(kp, size), NULL, 0);
522*50997Sbostic 	if (!rbufp)
523*50997Sbostic 		return (ERROR);
52446457Sbostic 	save_bufp = rbufp;
52546366Sbostic 
52646457Sbostic 	/* Pin the bucket chain */
52746457Sbostic 	rbufp->flags |= BUF_PIN;
528*50997Sbostic 	for (bp = (u_short *)rbufp->page, n = *bp++, ndx = 1; ndx < n;)
529*50997Sbostic 		if (bp[1] >= REAL_KEY) {
530*50997Sbostic 			/* Real key/data pair */
531*50997Sbostic 			if (size == off - *bp &&
532*50997Sbostic 			    bcmp(kp, rbufp->page + *bp, size) == 0)
533*50997Sbostic 				goto found;
534*50997Sbostic 			off = bp[1];
53546366Sbostic #ifdef HASH_STATISTICS
536*50997Sbostic 			hash_collisions++;
53746366Sbostic #endif
538*50997Sbostic 			bp += 2;
539*50997Sbostic 			ndx += 2;
540*50997Sbostic 		} else if (bp[1] == OVFLPAGE) {
541*50997Sbostic 			rbufp = __get_buf(*bp, rbufp, 0);
542*50997Sbostic 			if (!rbufp) {
543*50997Sbostic 				save_bufp->flags &= ~BUF_PIN;
544*50997Sbostic 				return (ERROR);
545*50997Sbostic 			}
546*50997Sbostic 			/* FOR LOOP INIT */
547*50997Sbostic 			bp = (u_short *)rbufp->page;
548*50997Sbostic 			n = *bp++;
549*50997Sbostic 			ndx = 1;
550*50997Sbostic 			off = hashp->BSIZE;
551*50997Sbostic 		} else if (bp[1] < REAL_KEY) {
552*50997Sbostic 			if ((ndx = __find_bigpair(rbufp, ndx, kp, size)) > 0)
553*50997Sbostic 				goto found;
554*50997Sbostic 			if (ndx == -2) {
555*50997Sbostic 				bufp = rbufp;
556*50997Sbostic 				if (!(pageno = __find_last_page(&bufp))) {
557*50997Sbostic 					ndx = 0;
558*50997Sbostic 					rbufp = bufp;
559*50997Sbostic 					break;	/* FOR */
560*50997Sbostic 				}
561*50997Sbostic 				rbufp = __get_buf(pageno, bufp, 0);
562*50997Sbostic 				if (!rbufp) {
563*50997Sbostic 					save_bufp->flags &= ~BUF_PIN;
564*50997Sbostic 					return (ERROR);
565*50997Sbostic 				}
566*50997Sbostic 				/* FOR LOOP INIT */
567*50997Sbostic 				bp = (u_short *)rbufp->page;
568*50997Sbostic 				n = *bp++;
569*50997Sbostic 				ndx = 1;
570*50997Sbostic 				off = hashp->BSIZE;
571*50997Sbostic 			} else {
572*50997Sbostic 				save_bufp->flags &= ~BUF_PIN;
573*50997Sbostic 				return (ERROR);
574*50997Sbostic 			}
57546457Sbostic 		}
57646366Sbostic 
57746366Sbostic 	/* Not found */
578*50997Sbostic 	switch (action) {
579*50997Sbostic 	case HASH_PUT:
580*50997Sbostic 	case HASH_PUTNEW:
58146366Sbostic 		if (__addel(rbufp, key, val)) {
582*50997Sbostic 			save_bufp->flags &= ~BUF_PIN;
583*50997Sbostic 			return (ERROR);
58446366Sbostic 		} else {
585*50997Sbostic 			save_bufp->flags &= ~BUF_PIN;
586*50997Sbostic 			return (SUCCESS);
58746366Sbostic 		}
588*50997Sbostic 	case HASH_GET:
589*50997Sbostic 	case HASH_DELETE:
590*50997Sbostic 	default:
59146457Sbostic 		save_bufp->flags &= ~BUF_PIN;
592*50997Sbostic 		return (ABNORMAL);
59346366Sbostic 	}
59446366Sbostic 
59546366Sbostic found:
59646366Sbostic 	switch (action) {
597*50997Sbostic 	case HASH_PUTNEW:
59846457Sbostic 		save_bufp->flags &= ~BUF_PIN;
599*50997Sbostic 		return (ABNORMAL);
600*50997Sbostic 	case HASH_GET:
60146366Sbostic 		bp = (u_short *)rbufp->page;
602*50997Sbostic 		if (bp[ndx + 1] < REAL_KEY)
603*50997Sbostic 			__big_return(rbufp, ndx, val, 0);
60446366Sbostic 		else {
605*50997Sbostic 			val->data = (u_char *)rbufp->page + (int)bp[ndx + 1];
606*50997Sbostic 			val->size = bp[ndx] - bp[ndx + 1];
60746366Sbostic 		}
60846366Sbostic 		break;
609*50997Sbostic 	case HASH_PUT:
610*50997Sbostic 		if ((__delpair(rbufp, ndx)) || (__addel(rbufp, key, val))) {
611*50997Sbostic 			save_bufp->flags &= ~BUF_PIN;
612*50997Sbostic 			return (ERROR);
61346457Sbostic 		}
61446366Sbostic 		break;
615*50997Sbostic 	case HASH_DELETE:
616*50997Sbostic 		if (__delpair(rbufp, ndx))
617*50997Sbostic 			return (ERROR);
61846366Sbostic 		break;
61946366Sbostic 	}
62046457Sbostic 	save_bufp->flags &= ~BUF_PIN;
62146366Sbostic 	return (SUCCESS);
62246366Sbostic }
62346366Sbostic 
62446366Sbostic static int
62546366Sbostic hash_seq(dbp, key, data, flag)
626*50997Sbostic 	const DB *dbp;
627*50997Sbostic 	DBT *key, *data;
628*50997Sbostic 	u_int flag;
62946366Sbostic {
630*50997Sbostic 	register u_int bucket;
631*50997Sbostic 	register BUFHEAD *bufp;
632*50997Sbostic 	u_short *bp, ndx;
63346366Sbostic 
634*50997Sbostic 	if (flag && flag != R_FIRST && flag != R_NEXT) {
635*50997Sbostic 		hashp->errno = errno = EINVAL;
636*50997Sbostic 		return (ERROR);
63746366Sbostic 	}
63846366Sbostic 	hashp = (HTAB *)dbp->internal;
639*50997Sbostic 	if (hashp->flags & O_WRONLY) {
640*50997Sbostic 		hashp->errno = errno = EPERM;
641*50997Sbostic 		return (ERROR);
64246366Sbostic 	}
64346366Sbostic #ifdef HASH_STATISTICS
64446366Sbostic 	hash_accesses++;
64546366Sbostic #endif
646*50997Sbostic 	if ((hashp->cbucket < 0) || (flag == R_FIRST)) {
647*50997Sbostic 		hashp->cbucket = 0;
648*50997Sbostic 		hashp->cndx = 1;
649*50997Sbostic 		hashp->cpage = NULL;
65046366Sbostic 	}
651*50997Sbostic 	if (!(bufp = hashp->cpage)) {
652*50997Sbostic 		for (bucket = hashp->cbucket; bucket <= hashp->MAX_BUCKET;
653*50997Sbostic 		    bucket++, hashp->cndx = 1) {
65446366Sbostic 
655*50997Sbostic 			bufp = __get_buf(bucket, NULL, 0);
656*50997Sbostic 			if (!bufp)
657*50997Sbostic 				return (ERROR);
658*50997Sbostic 			hashp->cpage = bufp;
659*50997Sbostic 			bp = (u_short *)bufp->page;
660*50997Sbostic 			if (bp[0])
661*50997Sbostic 				break;
662*50997Sbostic 		}
663*50997Sbostic 		hashp->cbucket = bucket;
664*50997Sbostic 		if (hashp->cbucket > hashp->MAX_BUCKET) {
665*50997Sbostic 			hashp->cbucket = -1;
666*50997Sbostic 			return (ABNORMAL);
667*50997Sbostic 		}
668*50997Sbostic 	} else
669*50997Sbostic 		bp = (u_short *)hashp->cpage->page;
67046366Sbostic 
67146366Sbostic #ifdef DEBUG
672*50997Sbostic 	assert(bp);
673*50997Sbostic 	assert(bufp);
67446366Sbostic #endif
675*50997Sbostic 	while (bp[hashp->cndx + 1] == OVFLPAGE) {
676*50997Sbostic 		bufp = hashp->cpage = __get_buf(bp[hashp->cndx], bufp, 0);
677*50997Sbostic 		if (!bufp)
678*50997Sbostic 			return (ERROR);
679*50997Sbostic 		bp = (u_short *)(bufp->page);
680*50997Sbostic 		hashp->cndx = 1;
68146366Sbostic 	}
68246366Sbostic 	ndx = hashp->cndx;
683*50997Sbostic 	if (bp[ndx + 1] < REAL_KEY) {
684*50997Sbostic 		if (__big_keydata(bufp, ndx, key, data, 1))
685*50997Sbostic 			return (ERROR);
68646366Sbostic 	} else {
687*50997Sbostic 		key->data = (u_char *)hashp->cpage->page + bp[ndx];
688*50997Sbostic 		key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx];
689*50997Sbostic 		data->data = (u_char *)hashp->cpage->page + bp[ndx + 1];
690*50997Sbostic 		data->size = bp[ndx] - bp[ndx + 1];
691*50997Sbostic 		ndx += 2;
692*50997Sbostic 		if (ndx > bp[0]) {
693*50997Sbostic 			hashp->cpage = NULL;
694*50997Sbostic 			hashp->cbucket++;
695*50997Sbostic 			hashp->cndx = 1;
696*50997Sbostic 		} else
697*50997Sbostic 			hashp->cndx = ndx;
69846366Sbostic 	}
69946366Sbostic 	return (SUCCESS);
70046366Sbostic }
70146366Sbostic 
70246366Sbostic /********************************* UTILITIES ************************/
703*50997Sbostic 
70446366Sbostic /*
705*50997Sbostic  * Returns:
706*50997Sbostic  *	 0 ==> OK
707*50997Sbostic  *	-1 ==> Error
708*50997Sbostic  */
70946366Sbostic extern int
71046366Sbostic __expand_table()
71146366Sbostic {
712*50997Sbostic 	u_int old_bucket, new_bucket;
713*50997Sbostic 	int dirsize, new_segnum, spare_ndx;
714*50997Sbostic 
71546366Sbostic #ifdef HASH_STATISTICS
71646366Sbostic 	hash_expansions++;
71746366Sbostic #endif
71846366Sbostic 	new_bucket = ++hashp->MAX_BUCKET;
71946366Sbostic 	old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK);
72046366Sbostic 
72146366Sbostic 	new_segnum = new_bucket >> hashp->SSHIFT;
72246366Sbostic 
72346366Sbostic 	/* Check if we need a new segment */
724*50997Sbostic 	if (new_segnum >= hashp->nsegs) {
725*50997Sbostic 		/* Check if we need to expand directory */
726*50997Sbostic 		if (new_segnum >= hashp->DSIZE) {
727*50997Sbostic 			/* Reallocate directory */
728*50997Sbostic 			dirsize = hashp->DSIZE * sizeof(SEGMENT *);
729*50997Sbostic 			if (!hash_realloc(&hashp->dir, dirsize, dirsize << 1))
730*50997Sbostic 				return (-1);
731*50997Sbostic 			hashp->DSIZE = dirsize << 1;
73246366Sbostic 		}
733*50997Sbostic 		if (!(hashp->dir[new_segnum] =
734*50997Sbostic 			calloc(hashp->SGSIZE, sizeof(SEGMENT))))
735*50997Sbostic 			return (-1);
736*50997Sbostic 		hashp->exsegs++;
737*50997Sbostic 		hashp->nsegs++;
73846366Sbostic 	}
73946366Sbostic 	/*
740*50997Sbostic 	 * If the split point is increasing (MAX_BUCKET's log base 2
741*50997Sbostic 	 * * increases), we need to copy the current contents of the spare
742*50997Sbostic 	 * split bucket to the next bucket.
743*50997Sbostic 	 */
74446366Sbostic 	spare_ndx = __log2(hashp->MAX_BUCKET);
745*50997Sbostic 	if (spare_ndx != (__log2(hashp->MAX_BUCKET - 1)))
746*50997Sbostic 		hashp->SPARES[spare_ndx] = hashp->SPARES[spare_ndx - 1];
747*50997Sbostic 	if (new_bucket > hashp->HIGH_MASK) {
748*50997Sbostic 		/* Starting a new doubling */
749*50997Sbostic 		hashp->LOW_MASK = hashp->HIGH_MASK;
750*50997Sbostic 		hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK;
75146366Sbostic 	}
752*50997Sbostic 	/* Relocate records to the new bucket */
753*50997Sbostic 	return (__split_page(old_bucket, new_bucket));
754*50997Sbostic }
75546366Sbostic 
75646366Sbostic /*
757*50997Sbostic  * If realloc guarantees that the pointer is not destroyed if the realloc
758*50997Sbostic  * fails, then this routine can go away.
759*50997Sbostic  */
760*50997Sbostic static void *
761*50997Sbostic hash_realloc(p_ptr, oldsize, newsize)
762*50997Sbostic 	SEGMENT **p_ptr;
763*50997Sbostic 	int oldsize, newsize;
76446366Sbostic {
765*50997Sbostic 	register void *p;
76646366Sbostic 
767*50997Sbostic 	if (p = malloc(newsize)) {
768*50997Sbostic 		bcopy(*p_ptr, p, oldsize);
769*50997Sbostic 		bzero(*p_ptr + oldsize, newsize - oldsize);
77046366Sbostic 		free(*p_ptr);
77146366Sbostic 		*p_ptr = p;
77246366Sbostic 	}
77346366Sbostic 	return (p);
77446366Sbostic }
77546366Sbostic 
77647251Sbostic extern u_int
777*50997Sbostic __call_hash(k, len)
778*50997Sbostic 	char *k;
779*50997Sbostic 	int len;
78046366Sbostic {
781*50997Sbostic 	int n, bucket;
782*50997Sbostic 
78346366Sbostic 	n = hashp->hash(k, len);
78446366Sbostic 	bucket = n & hashp->HIGH_MASK;
785*50997Sbostic 	if (bucket > hashp->MAX_BUCKET)
786*50997Sbostic 		bucket = bucket & hashp->LOW_MASK;
787*50997Sbostic 	return (bucket);
78846366Sbostic }
78946366Sbostic 
79046366Sbostic /*
791*50997Sbostic  * Allocate segment table.  On error, destroy the table and set errno.
792*50997Sbostic  *
793*50997Sbostic  * Returns 0 on success
794*50997Sbostic  */
79546366Sbostic static int
796*50997Sbostic alloc_segs(nsegs)
797*50997Sbostic 	int nsegs;
79846366Sbostic {
799*50997Sbostic 	register int i;
800*50997Sbostic 	register SEGMENT store;
80146366Sbostic 
802*50997Sbostic 	int save_errno;
80346366Sbostic 
804*50997Sbostic 	if (!(hashp->dir = calloc(hashp->DSIZE, sizeof(SEGMENT *)))) {
805*50997Sbostic 		save_errno = errno;
806*50997Sbostic 		(void)hdestroy();
807*50997Sbostic 		errno = save_errno;
808*50997Sbostic 		return (-1);
809*50997Sbostic 	}
810*50997Sbostic 	/* Allocate segments */
811*50997Sbostic 	store = calloc(nsegs << hashp->SSHIFT, sizeof(SEGMENT));
812*50997Sbostic 	if (!store) {
813*50997Sbostic 		save_errno = errno;
814*50997Sbostic 		(void)hdestroy();
815*50997Sbostic 		errno = save_errno;
816*50997Sbostic 		return (-1);
817*50997Sbostic 	}
818*50997Sbostic 	for (i = 0; i < nsegs; i++, hashp->nsegs++)
819*50997Sbostic 		hashp->dir[i] = &store[i << hashp->SSHIFT];
820*50997Sbostic 	return (0);
82146366Sbostic }
82246366Sbostic 
823*50997Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
82446366Sbostic /*
825*50997Sbostic  * Hashp->hdr needs to be byteswapped.
826*50997Sbostic  */
82746366Sbostic static void
828*50997Sbostic swap_header_copy(srcp, destp)
829*50997Sbostic 	HASHHDR *srcp, *destp;
83046366Sbostic {
831*50997Sbostic 	int i;
83246366Sbostic 
833*50997Sbostic 	BLSWAP_COPY(srcp->magic, destp->magic);
834*50997Sbostic 	BLSWAP_COPY(srcp->version, destp->version);
835*50997Sbostic 	BLSWAP_COPY(srcp->lorder, destp->lorder);
836*50997Sbostic 	BLSWAP_COPY(srcp->bsize, destp->bsize);
837*50997Sbostic 	BLSWAP_COPY(srcp->bshift, destp->bshift);
838*50997Sbostic 	BLSWAP_COPY(srcp->dsize, destp->dsize);
839*50997Sbostic 	BLSWAP_COPY(srcp->ssize, destp->ssize);
840*50997Sbostic 	BLSWAP_COPY(srcp->sshift, destp->sshift);
841*50997Sbostic 	BLSWAP_COPY(srcp->max_bucket, destp->max_bucket);
842*50997Sbostic 	BLSWAP_COPY(srcp->high_mask, destp->high_mask);
843*50997Sbostic 	BLSWAP_COPY(srcp->low_mask, destp->low_mask);
844*50997Sbostic 	BLSWAP_COPY(srcp->ffactor, destp->ffactor);
845*50997Sbostic 	BLSWAP_COPY(srcp->nkeys, destp->nkeys);
846*50997Sbostic 	BLSWAP_COPY(srcp->hdrpages, destp->hdrpages);
847*50997Sbostic 	BLSWAP_COPY(srcp->h_charkey, destp->h_charkey);
848*50997Sbostic 	for (i = 0; i < NCACHED; i++) {
849*50997Sbostic 		BLSWAP_COPY(srcp->spares[i], destp->spares[i]);
850*50997Sbostic 		BSSWAP_COPY(srcp->bitmaps[i], destp->bitmaps[i]);
851*50997Sbostic 	}
85246366Sbostic }
85346366Sbostic 
85446366Sbostic static void
855*50997Sbostic swap_header()
85646366Sbostic {
857*50997Sbostic 	HASHHDR *hdrp;
858*50997Sbostic 	int i;
85946366Sbostic 
860*50997Sbostic 	hdrp = &hashp->hdr;
86146366Sbostic 
862*50997Sbostic 	BLSWAP(hdrp->magic);
863*50997Sbostic 	BLSWAP(hdrp->version);
864*50997Sbostic 	BLSWAP(hdrp->lorder);
865*50997Sbostic 	BLSWAP(hdrp->bsize);
866*50997Sbostic 	BLSWAP(hdrp->bshift);
867*50997Sbostic 	BLSWAP(hdrp->dsize);
868*50997Sbostic 	BLSWAP(hdrp->ssize);
869*50997Sbostic 	BLSWAP(hdrp->sshift);
870*50997Sbostic 	BLSWAP(hdrp->max_bucket);
871*50997Sbostic 	BLSWAP(hdrp->high_mask);
872*50997Sbostic 	BLSWAP(hdrp->low_mask);
873*50997Sbostic 	BLSWAP(hdrp->ffactor);
874*50997Sbostic 	BLSWAP(hdrp->nkeys);
875*50997Sbostic 	BLSWAP(hdrp->hdrpages);
876*50997Sbostic 	BLSWAP(hdrp->h_charkey);
877*50997Sbostic 	for (i = 0; i < NCACHED; i++) {
878*50997Sbostic 		BLSWAP(hdrp->spares[i]);
879*50997Sbostic 		BSSWAP(hdrp->bitmaps[i]);
880*50997Sbostic 	}
88146366Sbostic }
882*50997Sbostic #endif
883