xref: /openbsd-src/lib/libc/db/hash/hash.c (revision 3240e6a8f93e9dcb3b95e7fafb9e2b27f13c7c9d)
1*3240e6a8Sguenther /*	$OpenBSD: hash.c,v 1.29 2016/09/21 04:38:56 guenther Exp $	*/
21b727fc6Smillert 
3df930be7Sderaadt /*-
4df930be7Sderaadt  * Copyright (c) 1990, 1993, 1994
5df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
6df930be7Sderaadt  *
7df930be7Sderaadt  * This code is derived from software contributed to Berkeley by
8df930be7Sderaadt  * Margo Seltzer.
9df930be7Sderaadt  *
10df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
11df930be7Sderaadt  * modification, are permitted provided that the following conditions
12df930be7Sderaadt  * are met:
13df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
14df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
15df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
16df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
17df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
186580fee3Smillert  * 3. Neither the name of the University nor the names of its contributors
19df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
20df930be7Sderaadt  *    without specific prior written permission.
21df930be7Sderaadt  *
22df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32df930be7Sderaadt  * SUCH DAMAGE.
33df930be7Sderaadt  */
34df930be7Sderaadt 
35df930be7Sderaadt #include <sys/stat.h>
36df930be7Sderaadt 
37df930be7Sderaadt #include <errno.h>
38df930be7Sderaadt #include <fcntl.h>
39df930be7Sderaadt #include <stdio.h>
40df930be7Sderaadt #include <stdlib.h>
41df930be7Sderaadt #include <string.h>
42df930be7Sderaadt #include <unistd.h>
43df930be7Sderaadt #ifdef DEBUG
44df930be7Sderaadt #include <assert.h>
45df930be7Sderaadt #endif
46df930be7Sderaadt 
47df930be7Sderaadt #include <db.h>
48df930be7Sderaadt #include "hash.h"
49df930be7Sderaadt #include "page.h"
50df930be7Sderaadt #include "extern.h"
51df930be7Sderaadt 
52aea60beeSderaadt #define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
53aea60beeSderaadt 
54c72b5b24Smillert static int   alloc_segs(HTAB *, int);
55c72b5b24Smillert static int   flush_meta(HTAB *);
56c72b5b24Smillert static int   hash_access(HTAB *, ACTION, DBT *, DBT *);
57c72b5b24Smillert static int   hash_close(DB *);
58c72b5b24Smillert static int   hash_delete(const DB *, const DBT *, u_int32_t);
59c72b5b24Smillert static int   hash_fd(const DB *);
60c72b5b24Smillert static int   hash_get(const DB *, const DBT *, DBT *, u_int32_t);
61c72b5b24Smillert static int   hash_put(const DB *, DBT *, const DBT *, u_int32_t);
62c72b5b24Smillert static void *hash_realloc(SEGMENT **, int, int);
63c72b5b24Smillert static int   hash_seq(const DB *, DBT *, DBT *, u_int32_t);
64c72b5b24Smillert static int   hash_sync(const DB *, u_int32_t);
65c72b5b24Smillert static int   hdestroy(HTAB *);
66f5f3a36bSguenther static HTAB *init_hash(HTAB *, const char *, const HASHINFO *);
67c72b5b24Smillert static int   init_htab(HTAB *, int);
68df930be7Sderaadt #if BYTE_ORDER == LITTLE_ENDIAN
69c72b5b24Smillert static void  swap_header(HTAB *);
70c72b5b24Smillert static void  swap_header_copy(HASHHDR *, HASHHDR *);
71df930be7Sderaadt #endif
72df930be7Sderaadt 
73df930be7Sderaadt /* Fast arithmetic, relying on powers of 2, */
74df930be7Sderaadt #define MOD(x, y)		((x) & ((y) - 1))
75df930be7Sderaadt 
76df930be7Sderaadt #define RETURN_ERROR(ERR, LOC)	{ save_errno = ERR; goto LOC; }
77df930be7Sderaadt 
78df930be7Sderaadt /* Return values */
79df930be7Sderaadt #define	SUCCESS	 (0)
80df930be7Sderaadt #define	ERROR	(-1)
81df930be7Sderaadt #define	ABNORMAL (1)
82df930be7Sderaadt 
83df930be7Sderaadt #ifdef HASH_STATISTICS
84df930be7Sderaadt int hash_accesses, hash_collisions, hash_expansions, hash_overflows;
85df930be7Sderaadt #endif
86df930be7Sderaadt 
87df930be7Sderaadt /************************** INTERFACE ROUTINES ***************************/
88df930be7Sderaadt /* OPEN/CLOSE */
89df930be7Sderaadt 
90e20a56a5Sotto DB *
__hash_open(const char * file,int flags,int mode,const HASHINFO * info,int dflags)91e20a56a5Sotto __hash_open(const char *file, int flags, int mode,
92e20a56a5Sotto     const HASHINFO *info,	/* Special directives for create */
93e20a56a5Sotto     int dflags)
94df930be7Sderaadt {
95df930be7Sderaadt 	HTAB *hashp;
96df930be7Sderaadt 	struct stat statbuf;
97df930be7Sderaadt 	DB *dbp;
989a1282d0Smillert 	int bpages, hdrsize, new_table, nsegs, save_errno;
99df930be7Sderaadt 
100df930be7Sderaadt 	if ((flags & O_ACCMODE) == O_WRONLY) {
101df930be7Sderaadt 		errno = EINVAL;
102df930be7Sderaadt 		return (NULL);
103df930be7Sderaadt 	}
104df95a199Smillert 
105df930be7Sderaadt 	if (!(hashp = (HTAB *)calloc(1, sizeof(HTAB))))
106df930be7Sderaadt 		return (NULL);
107df930be7Sderaadt 	hashp->fp = -1;
108df930be7Sderaadt 
109df930be7Sderaadt 	/*
110df930be7Sderaadt 	 * Even if user wants write only, we need to be able to read
111df930be7Sderaadt 	 * the actual file, so we need to open it read/write. But, the
112df930be7Sderaadt 	 * field in the hashp structure needs to be accurate so that
113df930be7Sderaadt 	 * we can check accesses.
114df930be7Sderaadt 	 */
115df930be7Sderaadt 	hashp->flags = flags;
116df930be7Sderaadt 
117df930be7Sderaadt 	if (file) {
1180205f8e6Sguenther 		if ((hashp->fp = open(file, flags | O_CLOEXEC, mode)) == -1)
119df930be7Sderaadt 			RETURN_ERROR(errno, error0);
1209a1282d0Smillert 		new_table = fstat(hashp->fp, &statbuf) == 0 &&
1219a1282d0Smillert 		    statbuf.st_size == 0 && (flags & O_ACCMODE) != O_RDONLY;
1229a1282d0Smillert 	} else
1239a1282d0Smillert 		new_table = 1;
1249a1282d0Smillert 
125df930be7Sderaadt 	if (new_table) {
126f5f3a36bSguenther 		if (!(hashp = init_hash(hashp, file, info)))
127df930be7Sderaadt 			RETURN_ERROR(errno, error1);
128df930be7Sderaadt 	} else {
129df930be7Sderaadt 		/* Table already exists */
130df930be7Sderaadt 		if (info && info->hash)
131df930be7Sderaadt 			hashp->hash = info->hash;
132df930be7Sderaadt 		else
133df930be7Sderaadt 			hashp->hash = __default_hash;
134df930be7Sderaadt 
135df930be7Sderaadt 		hdrsize = read(hashp->fp, &hashp->hdr, sizeof(HASHHDR));
136df930be7Sderaadt #if BYTE_ORDER == LITTLE_ENDIAN
137df930be7Sderaadt 		swap_header(hashp);
138df930be7Sderaadt #endif
139df930be7Sderaadt 		if (hdrsize == -1)
140df930be7Sderaadt 			RETURN_ERROR(errno, error1);
141df930be7Sderaadt 		if (hdrsize != sizeof(HASHHDR))
142df930be7Sderaadt 			RETURN_ERROR(EFTYPE, error1);
143df930be7Sderaadt 		/* Verify file type, versions and hash function */
144df930be7Sderaadt 		if (hashp->MAGIC != HASHMAGIC)
145df930be7Sderaadt 			RETURN_ERROR(EFTYPE, error1);
146df930be7Sderaadt #define	OLDHASHVERSION	1
147df930be7Sderaadt 		if (hashp->VERSION != HASHVERSION &&
148df930be7Sderaadt 		    hashp->VERSION != OLDHASHVERSION)
149df930be7Sderaadt 			RETURN_ERROR(EFTYPE, error1);
150df930be7Sderaadt 		if (hashp->hash(CHARKEY, sizeof(CHARKEY)) != hashp->H_CHARKEY)
151df930be7Sderaadt 			RETURN_ERROR(EFTYPE, error1);
152df930be7Sderaadt 		/*
153df930be7Sderaadt 		 * Figure out how many segments we need.  Max_Bucket is the
154df930be7Sderaadt 		 * maximum bucket number, so the number of buckets is
155df930be7Sderaadt 		 * max_bucket + 1.
156df930be7Sderaadt 		 */
157df930be7Sderaadt 		nsegs = (hashp->MAX_BUCKET + 1 + hashp->SGSIZE - 1) /
158df930be7Sderaadt 			 hashp->SGSIZE;
159df930be7Sderaadt 		if (alloc_segs(hashp, nsegs))
160df930be7Sderaadt 			/*
161df930be7Sderaadt 			 * If alloc_segs fails, table will have been destroyed
162df930be7Sderaadt 			 * and errno will have been set.
163df930be7Sderaadt 			 */
164df930be7Sderaadt 			return (NULL);
165df930be7Sderaadt 		/* Read in bitmaps */
166df930be7Sderaadt 		bpages = (hashp->SPARES[hashp->OVFL_POINT] +
167df930be7Sderaadt 		    (hashp->BSIZE << BYTE_SHIFT) - 1) >>
168df930be7Sderaadt 		    (hashp->BSHIFT + BYTE_SHIFT);
169df930be7Sderaadt 
170df930be7Sderaadt 		hashp->nmaps = bpages;
171df930be7Sderaadt 		(void)memset(&hashp->mapp[0], 0, bpages * sizeof(u_int32_t *));
172df930be7Sderaadt 	}
173df930be7Sderaadt 
174df930be7Sderaadt 	/* Initialize Buffer Manager */
175df930be7Sderaadt 	if (info && info->cachesize)
176df930be7Sderaadt 		__buf_init(hashp, info->cachesize);
177df930be7Sderaadt 	else
178df930be7Sderaadt 		__buf_init(hashp, DEF_BUFSIZE);
179df930be7Sderaadt 
180df930be7Sderaadt 	hashp->new_file = new_table;
181df930be7Sderaadt 	hashp->save_file = file && (hashp->flags & O_RDWR);
182df930be7Sderaadt 	hashp->cbucket = -1;
183df930be7Sderaadt 	if (!(dbp = (DB *)malloc(sizeof(DB)))) {
184df930be7Sderaadt 		save_errno = errno;
185df930be7Sderaadt 		hdestroy(hashp);
186df930be7Sderaadt 		errno = save_errno;
187df930be7Sderaadt 		return (NULL);
188df930be7Sderaadt 	}
189df930be7Sderaadt 	dbp->internal = hashp;
190df930be7Sderaadt 	dbp->close = hash_close;
191df930be7Sderaadt 	dbp->del = hash_delete;
192df930be7Sderaadt 	dbp->fd = hash_fd;
193df930be7Sderaadt 	dbp->get = hash_get;
194df930be7Sderaadt 	dbp->put = hash_put;
195df930be7Sderaadt 	dbp->seq = hash_seq;
196df930be7Sderaadt 	dbp->sync = hash_sync;
197df930be7Sderaadt 	dbp->type = DB_HASH;
198df930be7Sderaadt 
199df930be7Sderaadt #ifdef DEBUG
200df930be7Sderaadt 	(void)fprintf(stderr,
201df95a199Smillert "%s\n%s%p\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",
202df930be7Sderaadt 	    "init_htab:",
203df930be7Sderaadt 	    "TABLE POINTER   ", hashp,
204df930be7Sderaadt 	    "BUCKET SIZE     ", hashp->BSIZE,
205df930be7Sderaadt 	    "BUCKET SHIFT    ", hashp->BSHIFT,
206df930be7Sderaadt 	    "DIRECTORY SIZE  ", hashp->DSIZE,
207df930be7Sderaadt 	    "SEGMENT SIZE    ", hashp->SGSIZE,
208df930be7Sderaadt 	    "SEGMENT SHIFT   ", hashp->SSHIFT,
209df930be7Sderaadt 	    "FILL FACTOR     ", hashp->FFACTOR,
210df930be7Sderaadt 	    "MAX BUCKET      ", hashp->MAX_BUCKET,
211df930be7Sderaadt 	    "OVFL POINT	     ", hashp->OVFL_POINT,
212df930be7Sderaadt 	    "LAST FREED      ", hashp->LAST_FREED,
213df930be7Sderaadt 	    "HIGH MASK       ", hashp->HIGH_MASK,
214df930be7Sderaadt 	    "LOW  MASK       ", hashp->LOW_MASK,
215df930be7Sderaadt 	    "NSEGS	     ", hashp->nsegs,
216df930be7Sderaadt 	    "NKEYS	     ", hashp->NKEYS);
217df930be7Sderaadt #endif
218df930be7Sderaadt #ifdef HASH_STATISTICS
219df930be7Sderaadt 	hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0;
220df930be7Sderaadt #endif
221df930be7Sderaadt 	return (dbp);
222df930be7Sderaadt 
223df930be7Sderaadt error1:
224df930be7Sderaadt 	if (hashp != NULL)
225df930be7Sderaadt 		(void)close(hashp->fp);
226df930be7Sderaadt 
227df930be7Sderaadt error0:
228df930be7Sderaadt 	free(hashp);
229df930be7Sderaadt 	errno = save_errno;
230df930be7Sderaadt 	return (NULL);
231df930be7Sderaadt }
232df930be7Sderaadt 
233df930be7Sderaadt static int
hash_close(DB * dbp)234e20a56a5Sotto hash_close(DB *dbp)
235df930be7Sderaadt {
236df930be7Sderaadt 	HTAB *hashp;
237df930be7Sderaadt 	int retval;
238df930be7Sderaadt 
239df930be7Sderaadt 	if (!dbp)
240df930be7Sderaadt 		return (ERROR);
241df930be7Sderaadt 
242df930be7Sderaadt 	hashp = (HTAB *)dbp->internal;
243df930be7Sderaadt 	retval = hdestroy(hashp);
244df930be7Sderaadt 	free(dbp);
245df930be7Sderaadt 	return (retval);
246df930be7Sderaadt }
247df930be7Sderaadt 
248df930be7Sderaadt static int
hash_fd(const DB * dbp)249e20a56a5Sotto hash_fd(const DB *dbp)
250df930be7Sderaadt {
251df930be7Sderaadt 	HTAB *hashp;
252df930be7Sderaadt 
253df930be7Sderaadt 	if (!dbp)
254df930be7Sderaadt 		return (ERROR);
255df930be7Sderaadt 
256df930be7Sderaadt 	hashp = (HTAB *)dbp->internal;
257df930be7Sderaadt 	if (hashp->fp == -1) {
258df930be7Sderaadt 		errno = ENOENT;
259df930be7Sderaadt 		return (-1);
260df930be7Sderaadt 	}
261df930be7Sderaadt 	return (hashp->fp);
262df930be7Sderaadt }
263df930be7Sderaadt 
264df930be7Sderaadt /************************** LOCAL CREATION ROUTINES **********************/
265df930be7Sderaadt static HTAB *
init_hash(HTAB * hashp,const char * file,const HASHINFO * info)266f5f3a36bSguenther init_hash(HTAB *hashp, const char *file, const HASHINFO *info)
267df930be7Sderaadt {
268df930be7Sderaadt 	struct stat statbuf;
269df930be7Sderaadt 	int nelem;
270df930be7Sderaadt 
271df930be7Sderaadt 	nelem = 1;
272df930be7Sderaadt 	hashp->NKEYS = 0;
273df930be7Sderaadt 	hashp->LORDER = BYTE_ORDER;
274df930be7Sderaadt 	hashp->BSIZE = DEF_BUCKET_SIZE;
275df930be7Sderaadt 	hashp->BSHIFT = DEF_BUCKET_SHIFT;
276df930be7Sderaadt 	hashp->SGSIZE = DEF_SEGSIZE;
277df930be7Sderaadt 	hashp->SSHIFT = DEF_SEGSIZE_SHIFT;
278df930be7Sderaadt 	hashp->DSIZE = DEF_DIRSIZE;
279df930be7Sderaadt 	hashp->FFACTOR = DEF_FFACTOR;
280df930be7Sderaadt 	hashp->hash = __default_hash;
281df930be7Sderaadt 	memset(hashp->SPARES, 0, sizeof(hashp->SPARES));
282df930be7Sderaadt 	memset(hashp->BITMAPS, 0, sizeof (hashp->BITMAPS));
283df930be7Sderaadt 
284df930be7Sderaadt 	/* Fix bucket size to be optimal for file system */
285df930be7Sderaadt 	if (file != NULL) {
286df930be7Sderaadt 		if (stat(file, &statbuf))
287df930be7Sderaadt 			return (NULL);
288df930be7Sderaadt 		hashp->BSIZE = statbuf.st_blksize;
289df930be7Sderaadt 		hashp->BSHIFT = __log2(hashp->BSIZE);
290df930be7Sderaadt 	}
291df930be7Sderaadt 
292df930be7Sderaadt 	if (info) {
293df930be7Sderaadt 		if (info->bsize) {
294df930be7Sderaadt 			/* Round pagesize up to power of 2 */
295df930be7Sderaadt 			hashp->BSHIFT = __log2(info->bsize);
296df930be7Sderaadt 			hashp->BSIZE = 1 << hashp->BSHIFT;
297df930be7Sderaadt 			if (hashp->BSIZE > MAX_BSIZE) {
298df930be7Sderaadt 				errno = EINVAL;
299df930be7Sderaadt 				return (NULL);
300df930be7Sderaadt 			}
301df930be7Sderaadt 		}
302df930be7Sderaadt 		if (info->ffactor)
303df930be7Sderaadt 			hashp->FFACTOR = info->ffactor;
304df930be7Sderaadt 		if (info->hash)
305df930be7Sderaadt 			hashp->hash = info->hash;
306df930be7Sderaadt 		if (info->nelem)
307df930be7Sderaadt 			nelem = info->nelem;
308df930be7Sderaadt 		if (info->lorder) {
309df930be7Sderaadt 			if (info->lorder != BIG_ENDIAN &&
310df930be7Sderaadt 			    info->lorder != LITTLE_ENDIAN) {
311df930be7Sderaadt 				errno = EINVAL;
312df930be7Sderaadt 				return (NULL);
313df930be7Sderaadt 			}
314df930be7Sderaadt 			hashp->LORDER = info->lorder;
315df930be7Sderaadt 		}
316df930be7Sderaadt 	}
317df930be7Sderaadt 	/* init_htab should destroy the table and set errno if it fails */
318df930be7Sderaadt 	if (init_htab(hashp, nelem))
319df930be7Sderaadt 		return (NULL);
320df930be7Sderaadt 	else
321df930be7Sderaadt 		return (hashp);
322df930be7Sderaadt }
323df930be7Sderaadt /*
324df930be7Sderaadt  * This calls alloc_segs which may run out of memory.  Alloc_segs will destroy
325df930be7Sderaadt  * the table and set errno, so we just pass the error information along.
326df930be7Sderaadt  *
327df930be7Sderaadt  * Returns 0 on No Error
328df930be7Sderaadt  */
329df930be7Sderaadt static int
init_htab(HTAB * hashp,int nelem)330e20a56a5Sotto init_htab(HTAB *hashp, int nelem)
331df930be7Sderaadt {
3326c51c909Smillert 	int nbuckets, nsegs, l2;
333df930be7Sderaadt 
334df930be7Sderaadt 	/*
335df930be7Sderaadt 	 * Divide number of elements by the fill factor and determine a
336df930be7Sderaadt 	 * desired number of buckets.  Allocate space for the next greater
337df930be7Sderaadt 	 * power of two number of buckets.
338df930be7Sderaadt 	 */
339df930be7Sderaadt 	nelem = (nelem - 1) / hashp->FFACTOR + 1;
340df930be7Sderaadt 
341aea60beeSderaadt 	l2 = __log2(MAXIMUM(nelem, 2));
342df930be7Sderaadt 	nbuckets = 1 << l2;
343df930be7Sderaadt 
344df930be7Sderaadt 	hashp->SPARES[l2] = l2 + 1;
345df930be7Sderaadt 	hashp->SPARES[l2 + 1] = l2 + 1;
346df930be7Sderaadt 	hashp->OVFL_POINT = l2;
347df930be7Sderaadt 	hashp->LAST_FREED = 2;
348df930be7Sderaadt 
349df930be7Sderaadt 	/* First bitmap page is at: splitpoint l2 page offset 1 */
350df930be7Sderaadt 	if (__ibitmap(hashp, OADDR_OF(l2, 1), l2 + 1, 0))
351df930be7Sderaadt 		return (-1);
352df930be7Sderaadt 
353df930be7Sderaadt 	hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1;
354df930be7Sderaadt 	hashp->HIGH_MASK = (nbuckets << 1) - 1;
355aea60beeSderaadt 	hashp->HDRPAGES = ((MAXIMUM(sizeof(HASHHDR), MINHDRSIZE) - 1) >>
356df930be7Sderaadt 	    hashp->BSHIFT) + 1;
357df930be7Sderaadt 
358df930be7Sderaadt 	nsegs = (nbuckets - 1) / hashp->SGSIZE + 1;
359df930be7Sderaadt 	nsegs = 1 << __log2(nsegs);
360df930be7Sderaadt 
361df930be7Sderaadt 	if (nsegs > hashp->DSIZE)
362df930be7Sderaadt 		hashp->DSIZE = nsegs;
363df930be7Sderaadt 	return (alloc_segs(hashp, nsegs));
364df930be7Sderaadt }
365df930be7Sderaadt 
366df930be7Sderaadt /********************** DESTROY/CLOSE ROUTINES ************************/
367df930be7Sderaadt 
368df930be7Sderaadt /*
369df930be7Sderaadt  * Flushes any changes to the file if necessary and destroys the hashp
370df930be7Sderaadt  * structure, freeing all allocated space.
371df930be7Sderaadt  */
372df930be7Sderaadt static int
hdestroy(HTAB * hashp)373e20a56a5Sotto hdestroy(HTAB *hashp)
374df930be7Sderaadt {
375df930be7Sderaadt 	int i, save_errno;
376df930be7Sderaadt 
377df930be7Sderaadt 	save_errno = 0;
378df930be7Sderaadt 
379df930be7Sderaadt #ifdef HASH_STATISTICS
380df930be7Sderaadt 	(void)fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n",
381df930be7Sderaadt 	    hash_accesses, hash_collisions);
382df930be7Sderaadt 	(void)fprintf(stderr, "hdestroy: expansions %ld\n",
383df930be7Sderaadt 	    hash_expansions);
384df930be7Sderaadt 	(void)fprintf(stderr, "hdestroy: overflows %ld\n",
385df930be7Sderaadt 	    hash_overflows);
386df930be7Sderaadt 	(void)fprintf(stderr, "keys %ld maxp %d segmentcount %d\n",
387df930be7Sderaadt 	    hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs);
388df930be7Sderaadt 
389df930be7Sderaadt 	for (i = 0; i < NCACHED; i++)
390df930be7Sderaadt 		(void)fprintf(stderr,
391df930be7Sderaadt 		    "spares[%d] = %d\n", i, hashp->SPARES[i]);
392df930be7Sderaadt #endif
393df930be7Sderaadt 	/*
394df930be7Sderaadt 	 * Call on buffer manager to free buffers, and if required,
395df930be7Sderaadt 	 * write them to disk.
396df930be7Sderaadt 	 */
397df930be7Sderaadt 	if (__buf_free(hashp, 1, hashp->save_file))
398df930be7Sderaadt 		save_errno = errno;
399df930be7Sderaadt 	if (hashp->dir) {
400df930be7Sderaadt 		free(*hashp->dir);	/* Free initial segments */
401df930be7Sderaadt 		/* Free extra segments */
402df930be7Sderaadt 		while (hashp->exsegs--)
403df930be7Sderaadt 			free(hashp->dir[--hashp->nsegs]);
404df930be7Sderaadt 		free(hashp->dir);
405df930be7Sderaadt 	}
406df930be7Sderaadt 	if (flush_meta(hashp) && !save_errno)
407df930be7Sderaadt 		save_errno = errno;
408df930be7Sderaadt 	/* Free Bigmaps */
409df930be7Sderaadt 	for (i = 0; i < hashp->nmaps; i++)
410df930be7Sderaadt 		free(hashp->mapp[i]);
41158071879Smillert 	free(hashp->tmp_key);
41258071879Smillert 	free(hashp->tmp_buf);
413df930be7Sderaadt 
414df930be7Sderaadt 	if (hashp->fp != -1)
415df930be7Sderaadt 		(void)close(hashp->fp);
416df930be7Sderaadt 
417df930be7Sderaadt 	free(hashp);
418df930be7Sderaadt 
419df930be7Sderaadt 	if (save_errno) {
420df930be7Sderaadt 		errno = save_errno;
421df930be7Sderaadt 		return (ERROR);
422df930be7Sderaadt 	}
423df930be7Sderaadt 	return (SUCCESS);
424df930be7Sderaadt }
425df930be7Sderaadt /*
426df930be7Sderaadt  * Write modified pages to disk
427df930be7Sderaadt  *
428df930be7Sderaadt  * Returns:
429df930be7Sderaadt  *	 0 == OK
430df930be7Sderaadt  *	-1 ERROR
431df930be7Sderaadt  */
432df930be7Sderaadt static int
hash_sync(const DB * dbp,u_int32_t flags)433e20a56a5Sotto hash_sync(const DB *dbp, u_int32_t flags)
434df930be7Sderaadt {
435df930be7Sderaadt 	HTAB *hashp;
436df930be7Sderaadt 
437df930be7Sderaadt 	if (flags != 0) {
438df930be7Sderaadt 		errno = EINVAL;
439df930be7Sderaadt 		return (ERROR);
440df930be7Sderaadt 	}
441df930be7Sderaadt 
442df930be7Sderaadt 	if (!dbp)
443df930be7Sderaadt 		return (ERROR);
444df930be7Sderaadt 
445df930be7Sderaadt 	hashp = (HTAB *)dbp->internal;
446df930be7Sderaadt 	if (!hashp->save_file)
447df930be7Sderaadt 		return (0);
448df930be7Sderaadt 	if (__buf_free(hashp, 0, 1) || flush_meta(hashp))
449df930be7Sderaadt 		return (ERROR);
450df930be7Sderaadt 	hashp->new_file = 0;
451df930be7Sderaadt 	return (0);
452df930be7Sderaadt }
453df930be7Sderaadt 
454df930be7Sderaadt /*
455df930be7Sderaadt  * Returns:
456df930be7Sderaadt  *	 0 == OK
457df930be7Sderaadt  *	-1 indicates that errno should be set
458df930be7Sderaadt  */
459df930be7Sderaadt static int
flush_meta(HTAB * hashp)460e20a56a5Sotto flush_meta(HTAB *hashp)
461df930be7Sderaadt {
462df930be7Sderaadt 	HASHHDR *whdrp;
463df930be7Sderaadt #if BYTE_ORDER == LITTLE_ENDIAN
464df930be7Sderaadt 	HASHHDR whdr;
465df930be7Sderaadt #endif
466df930be7Sderaadt 	int fp, i, wsize;
467df930be7Sderaadt 
468df930be7Sderaadt 	if (!hashp->save_file)
469df930be7Sderaadt 		return (0);
470df930be7Sderaadt 	hashp->MAGIC = HASHMAGIC;
471df930be7Sderaadt 	hashp->VERSION = HASHVERSION;
472df930be7Sderaadt 	hashp->H_CHARKEY = hashp->hash(CHARKEY, sizeof(CHARKEY));
473df930be7Sderaadt 
474df930be7Sderaadt 	fp = hashp->fp;
475df930be7Sderaadt 	whdrp = &hashp->hdr;
476df930be7Sderaadt #if BYTE_ORDER == LITTLE_ENDIAN
477df930be7Sderaadt 	whdrp = &whdr;
478df930be7Sderaadt 	swap_header_copy(&hashp->hdr, whdrp);
479df930be7Sderaadt #endif
480*3240e6a8Sguenther 	if ((wsize = pwrite(fp, whdrp, sizeof(HASHHDR), 0)) == -1)
481df930be7Sderaadt 		return (-1);
482df930be7Sderaadt 	else
483df930be7Sderaadt 		if (wsize != sizeof(HASHHDR)) {
484df930be7Sderaadt 			errno = EFTYPE;
485c7cc963cSmarc 			hashp->err = errno;
486df930be7Sderaadt 			return (-1);
487df930be7Sderaadt 		}
488df930be7Sderaadt 	for (i = 0; i < NCACHED; i++)
489df930be7Sderaadt 		if (hashp->mapp[i])
490df930be7Sderaadt 			if (__put_page(hashp, (char *)hashp->mapp[i],
491df930be7Sderaadt 				hashp->BITMAPS[i], 0, 1))
492df930be7Sderaadt 				return (-1);
493df930be7Sderaadt 	return (0);
494df930be7Sderaadt }
495df930be7Sderaadt 
496df930be7Sderaadt /*******************************SEARCH ROUTINES *****************************/
497df930be7Sderaadt /*
498df930be7Sderaadt  * All the access routines return
499df930be7Sderaadt  *
500df930be7Sderaadt  * Returns:
501df930be7Sderaadt  *	 0 on SUCCESS
502df930be7Sderaadt  *	 1 to indicate an external ERROR (i.e. key not found, etc)
503df930be7Sderaadt  *	-1 to indicate an internal ERROR (i.e. out of memory, etc)
504df930be7Sderaadt  */
505df930be7Sderaadt static int
hash_get(const DB * dbp,const DBT * key,DBT * data,u_int32_t flag)506e20a56a5Sotto hash_get(const DB *dbp, const DBT *key, DBT *data, u_int32_t flag)
507df930be7Sderaadt {
508df930be7Sderaadt 	HTAB *hashp;
509df930be7Sderaadt 
510df930be7Sderaadt 	hashp = (HTAB *)dbp->internal;
511df930be7Sderaadt 	if (flag) {
512c7cc963cSmarc 		hashp->err = errno = EINVAL;
513df930be7Sderaadt 		return (ERROR);
514df930be7Sderaadt 	}
515df930be7Sderaadt 	return (hash_access(hashp, HASH_GET, (DBT *)key, data));
516df930be7Sderaadt }
517df930be7Sderaadt 
518df930be7Sderaadt static int
hash_put(const DB * dbp,DBT * key,const DBT * data,u_int32_t flag)519e20a56a5Sotto hash_put(const DB *dbp, DBT *key, const DBT *data, u_int32_t flag)
520df930be7Sderaadt {
521df930be7Sderaadt 	HTAB *hashp;
522df930be7Sderaadt 
523df930be7Sderaadt 	hashp = (HTAB *)dbp->internal;
524df930be7Sderaadt 	if (flag && flag != R_NOOVERWRITE) {
525c7cc963cSmarc 		hashp->err = errno = EINVAL;
526df930be7Sderaadt 		return (ERROR);
527df930be7Sderaadt 	}
528df930be7Sderaadt 	if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
529c7cc963cSmarc 		hashp->err = errno = EPERM;
530df930be7Sderaadt 		return (ERROR);
531df930be7Sderaadt 	}
532df930be7Sderaadt 	return (hash_access(hashp, flag == R_NOOVERWRITE ?
533df930be7Sderaadt 	    HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data));
534df930be7Sderaadt }
535df930be7Sderaadt 
536df930be7Sderaadt static int
hash_delete(const DB * dbp,const DBT * key,u_int32_t flag)537e20a56a5Sotto hash_delete(const DB *dbp, const DBT *key,
538e20a56a5Sotto     u_int32_t flag)		/* Ignored */
539df930be7Sderaadt {
540df930be7Sderaadt 	HTAB *hashp;
541df930be7Sderaadt 
542df930be7Sderaadt 	hashp = (HTAB *)dbp->internal;
543df930be7Sderaadt 	if (flag && flag != R_CURSOR) {
544c7cc963cSmarc 		hashp->err = errno = EINVAL;
545df930be7Sderaadt 		return (ERROR);
546df930be7Sderaadt 	}
547df930be7Sderaadt 	if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
548c7cc963cSmarc 		hashp->err = errno = EPERM;
549df930be7Sderaadt 		return (ERROR);
550df930be7Sderaadt 	}
551df930be7Sderaadt 	return (hash_access(hashp, HASH_DELETE, (DBT *)key, NULL));
552df930be7Sderaadt }
553df930be7Sderaadt 
554df930be7Sderaadt /*
555df930be7Sderaadt  * Assume that hashp has been set in wrapper routine.
556df930be7Sderaadt  */
557df930be7Sderaadt static int
hash_access(HTAB * hashp,ACTION action,DBT * key,DBT * val)558e20a56a5Sotto hash_access(HTAB *hashp, ACTION action, DBT *key, DBT *val)
559df930be7Sderaadt {
5606c51c909Smillert 	BUFHEAD *rbufp;
561df930be7Sderaadt 	BUFHEAD *bufp, *save_bufp;
5626c51c909Smillert 	u_int16_t *bp;
5636c51c909Smillert 	int n, ndx, off, size;
5646c51c909Smillert 	char *kp;
565df930be7Sderaadt 	u_int16_t pageno;
566df930be7Sderaadt 
567df930be7Sderaadt #ifdef HASH_STATISTICS
568df930be7Sderaadt 	hash_accesses++;
569df930be7Sderaadt #endif
570df930be7Sderaadt 
571df930be7Sderaadt 	off = hashp->BSIZE;
572df930be7Sderaadt 	size = key->size;
573df930be7Sderaadt 	kp = (char *)key->data;
574df930be7Sderaadt 	rbufp = __get_buf(hashp, __call_hash(hashp, kp, size), NULL, 0);
575df930be7Sderaadt 	if (!rbufp)
576df930be7Sderaadt 		return (ERROR);
577df930be7Sderaadt 	save_bufp = rbufp;
578df930be7Sderaadt 
579df930be7Sderaadt 	/* Pin the bucket chain */
580df930be7Sderaadt 	rbufp->flags |= BUF_PIN;
581df930be7Sderaadt 	for (bp = (u_int16_t *)rbufp->page, n = *bp++, ndx = 1; ndx < n;)
582df930be7Sderaadt 		if (bp[1] >= REAL_KEY) {
583df930be7Sderaadt 			/* Real key/data pair */
584df930be7Sderaadt 			if (size == off - *bp &&
585df930be7Sderaadt 			    memcmp(kp, rbufp->page + *bp, size) == 0)
586df930be7Sderaadt 				goto found;
587df930be7Sderaadt 			off = bp[1];
588df930be7Sderaadt #ifdef HASH_STATISTICS
589df930be7Sderaadt 			hash_collisions++;
590df930be7Sderaadt #endif
591df930be7Sderaadt 			bp += 2;
592df930be7Sderaadt 			ndx += 2;
593df930be7Sderaadt 		} else if (bp[1] == OVFLPAGE) {
594df930be7Sderaadt 			rbufp = __get_buf(hashp, *bp, rbufp, 0);
595df930be7Sderaadt 			if (!rbufp) {
596df930be7Sderaadt 				save_bufp->flags &= ~BUF_PIN;
597df930be7Sderaadt 				return (ERROR);
598df930be7Sderaadt 			}
599df930be7Sderaadt 			/* FOR LOOP INIT */
600df930be7Sderaadt 			bp = (u_int16_t *)rbufp->page;
601df930be7Sderaadt 			n = *bp++;
602df930be7Sderaadt 			ndx = 1;
603df930be7Sderaadt 			off = hashp->BSIZE;
604df930be7Sderaadt 		} else if (bp[1] < REAL_KEY) {
605df930be7Sderaadt 			if ((ndx =
606df930be7Sderaadt 			    __find_bigpair(hashp, rbufp, ndx, kp, size)) > 0)
607df930be7Sderaadt 				goto found;
608df930be7Sderaadt 			if (ndx == -2) {
609df930be7Sderaadt 				bufp = rbufp;
610df930be7Sderaadt 				if (!(pageno =
611df930be7Sderaadt 				    __find_last_page(hashp, &bufp))) {
612df930be7Sderaadt 					ndx = 0;
613df930be7Sderaadt 					rbufp = bufp;
614df930be7Sderaadt 					break;	/* FOR */
615df930be7Sderaadt 				}
616df930be7Sderaadt 				rbufp = __get_buf(hashp, pageno, bufp, 0);
617df930be7Sderaadt 				if (!rbufp) {
618df930be7Sderaadt 					save_bufp->flags &= ~BUF_PIN;
619df930be7Sderaadt 					return (ERROR);
620df930be7Sderaadt 				}
621df930be7Sderaadt 				/* FOR LOOP INIT */
622df930be7Sderaadt 				bp = (u_int16_t *)rbufp->page;
623df930be7Sderaadt 				n = *bp++;
624df930be7Sderaadt 				ndx = 1;
625df930be7Sderaadt 				off = hashp->BSIZE;
626df930be7Sderaadt 			} else {
627df930be7Sderaadt 				save_bufp->flags &= ~BUF_PIN;
628df930be7Sderaadt 				return (ERROR);
629df930be7Sderaadt 			}
630df930be7Sderaadt 		}
631df930be7Sderaadt 
632df930be7Sderaadt 	/* Not found */
633df930be7Sderaadt 	switch (action) {
634df930be7Sderaadt 	case HASH_PUT:
635df930be7Sderaadt 	case HASH_PUTNEW:
636df930be7Sderaadt 		if (__addel(hashp, rbufp, key, val)) {
637df930be7Sderaadt 			save_bufp->flags &= ~BUF_PIN;
638df930be7Sderaadt 			return (ERROR);
639df930be7Sderaadt 		} else {
640df930be7Sderaadt 			save_bufp->flags &= ~BUF_PIN;
641df930be7Sderaadt 			return (SUCCESS);
642df930be7Sderaadt 		}
643df930be7Sderaadt 	case HASH_GET:
644df930be7Sderaadt 	case HASH_DELETE:
645df930be7Sderaadt 	default:
646df930be7Sderaadt 		save_bufp->flags &= ~BUF_PIN;
647df930be7Sderaadt 		return (ABNORMAL);
648df930be7Sderaadt 	}
649df930be7Sderaadt 
650df930be7Sderaadt found:
651df930be7Sderaadt 	switch (action) {
652df930be7Sderaadt 	case HASH_PUTNEW:
653df930be7Sderaadt 		save_bufp->flags &= ~BUF_PIN;
654df930be7Sderaadt 		return (ABNORMAL);
655df930be7Sderaadt 	case HASH_GET:
656df930be7Sderaadt 		bp = (u_int16_t *)rbufp->page;
657df930be7Sderaadt 		if (bp[ndx + 1] < REAL_KEY) {
658df930be7Sderaadt 			if (__big_return(hashp, rbufp, ndx, val, 0))
659df930be7Sderaadt 				return (ERROR);
660df930be7Sderaadt 		} else {
661df930be7Sderaadt 			val->data = (u_char *)rbufp->page + (int)bp[ndx + 1];
662df930be7Sderaadt 			val->size = bp[ndx] - bp[ndx + 1];
663df930be7Sderaadt 		}
664df930be7Sderaadt 		break;
665df930be7Sderaadt 	case HASH_PUT:
666df930be7Sderaadt 		if ((__delpair(hashp, rbufp, ndx)) ||
667df930be7Sderaadt 		    (__addel(hashp, rbufp, key, val))) {
668df930be7Sderaadt 			save_bufp->flags &= ~BUF_PIN;
669df930be7Sderaadt 			return (ERROR);
670df930be7Sderaadt 		}
671df930be7Sderaadt 		break;
672df930be7Sderaadt 	case HASH_DELETE:
673df930be7Sderaadt 		if (__delpair(hashp, rbufp, ndx))
674df930be7Sderaadt 			return (ERROR);
675df930be7Sderaadt 		break;
676df930be7Sderaadt 	default:
677df930be7Sderaadt 		abort();
678df930be7Sderaadt 	}
679df930be7Sderaadt 	save_bufp->flags &= ~BUF_PIN;
680df930be7Sderaadt 	return (SUCCESS);
681df930be7Sderaadt }
682df930be7Sderaadt 
683df930be7Sderaadt static int
hash_seq(const DB * dbp,DBT * key,DBT * data,u_int32_t flag)684e20a56a5Sotto hash_seq(const DB *dbp, DBT *key, DBT *data, u_int32_t flag)
685df930be7Sderaadt {
6866c51c909Smillert 	u_int32_t bucket;
6876c51c909Smillert 	BUFHEAD *bufp;
688df930be7Sderaadt 	HTAB *hashp;
689df930be7Sderaadt 	u_int16_t *bp, ndx;
690df930be7Sderaadt 
691df930be7Sderaadt 	hashp = (HTAB *)dbp->internal;
692df930be7Sderaadt 	if (flag && flag != R_FIRST && flag != R_NEXT) {
693c7cc963cSmarc 		hashp->err = errno = EINVAL;
694df930be7Sderaadt 		return (ERROR);
695df930be7Sderaadt 	}
696df930be7Sderaadt #ifdef HASH_STATISTICS
697df930be7Sderaadt 	hash_accesses++;
698df930be7Sderaadt #endif
699df930be7Sderaadt 	if ((hashp->cbucket < 0) || (flag == R_FIRST)) {
700df930be7Sderaadt 		hashp->cbucket = 0;
701df930be7Sderaadt 		hashp->cndx = 1;
702df930be7Sderaadt 		hashp->cpage = NULL;
703df930be7Sderaadt 	}
704344d848dSmillert  next_bucket:
705df930be7Sderaadt 	for (bp = NULL; !bp || !bp[0]; ) {
706df930be7Sderaadt 		if (!(bufp = hashp->cpage)) {
707df930be7Sderaadt 			for (bucket = hashp->cbucket;
708df930be7Sderaadt 			    bucket <= hashp->MAX_BUCKET;
709df930be7Sderaadt 			    bucket++, hashp->cndx = 1) {
710df930be7Sderaadt 				bufp = __get_buf(hashp, bucket, NULL, 0);
711df930be7Sderaadt 				if (!bufp)
712df930be7Sderaadt 					return (ERROR);
713df930be7Sderaadt 				hashp->cpage = bufp;
714df930be7Sderaadt 				bp = (u_int16_t *)bufp->page;
715df930be7Sderaadt 				if (bp[0])
716df930be7Sderaadt 					break;
717df930be7Sderaadt 			}
718df930be7Sderaadt 			hashp->cbucket = bucket;
719df930be7Sderaadt 			if (hashp->cbucket > hashp->MAX_BUCKET) {
720df930be7Sderaadt 				hashp->cbucket = -1;
721df930be7Sderaadt 				return (ABNORMAL);
722df930be7Sderaadt 			}
723344d848dSmillert 		} else {
724df930be7Sderaadt 			bp = (u_int16_t *)hashp->cpage->page;
725344d848dSmillert 			if (flag == R_NEXT) {
726344d848dSmillert 				hashp->cndx += 2;
727344d848dSmillert 				if (hashp->cndx > bp[0]) {
728344d848dSmillert 					hashp->cpage = NULL;
729344d848dSmillert 					hashp->cbucket++;
730344d848dSmillert 					hashp->cndx = 1;
731344d848dSmillert 					goto next_bucket;
732344d848dSmillert 				}
733344d848dSmillert 			}
734344d848dSmillert 		}
735df930be7Sderaadt 
736df930be7Sderaadt #ifdef DEBUG
737df930be7Sderaadt 		assert(bp);
738df930be7Sderaadt 		assert(bufp);
739df930be7Sderaadt #endif
740df930be7Sderaadt 		while (bp[hashp->cndx + 1] == OVFLPAGE) {
741df930be7Sderaadt 			bufp = hashp->cpage =
742df930be7Sderaadt 			    __get_buf(hashp, bp[hashp->cndx], bufp, 0);
743df930be7Sderaadt 			if (!bufp)
744df930be7Sderaadt 				return (ERROR);
745df930be7Sderaadt 			bp = (u_int16_t *)(bufp->page);
746df930be7Sderaadt 			hashp->cndx = 1;
747df930be7Sderaadt 		}
748df930be7Sderaadt 		if (!bp[0]) {
749df930be7Sderaadt 			hashp->cpage = NULL;
750df930be7Sderaadt 			++hashp->cbucket;
751df930be7Sderaadt 		}
752df930be7Sderaadt 	}
753df930be7Sderaadt 	ndx = hashp->cndx;
754df930be7Sderaadt 	if (bp[ndx + 1] < REAL_KEY) {
755df930be7Sderaadt 		if (__big_keydata(hashp, bufp, key, data, 1))
756df930be7Sderaadt 			return (ERROR);
757df930be7Sderaadt 	} else {
75807f46334Sotto 		if (hashp->cpage == 0)
75907f46334Sotto 			return (ERROR);
760df930be7Sderaadt 		key->data = (u_char *)hashp->cpage->page + bp[ndx];
761df930be7Sderaadt 		key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx];
762df930be7Sderaadt 		data->data = (u_char *)hashp->cpage->page + bp[ndx + 1];
763df930be7Sderaadt 		data->size = bp[ndx] - bp[ndx + 1];
764df930be7Sderaadt 	}
765df930be7Sderaadt 	return (SUCCESS);
766df930be7Sderaadt }
767df930be7Sderaadt 
768df930be7Sderaadt /********************************* UTILITIES ************************/
769df930be7Sderaadt 
770df930be7Sderaadt /*
771df930be7Sderaadt  * Returns:
772df930be7Sderaadt  *	 0 ==> OK
773df930be7Sderaadt  *	-1 ==> Error
774df930be7Sderaadt  */
775e20a56a5Sotto int
__expand_table(HTAB * hashp)776e20a56a5Sotto __expand_table(HTAB *hashp)
777df930be7Sderaadt {
778df930be7Sderaadt 	u_int32_t old_bucket, new_bucket;
779df930be7Sderaadt 	int dirsize, new_segnum, spare_ndx;
780df930be7Sderaadt 
781df930be7Sderaadt #ifdef HASH_STATISTICS
782df930be7Sderaadt 	hash_expansions++;
783df930be7Sderaadt #endif
784df930be7Sderaadt 	new_bucket = ++hashp->MAX_BUCKET;
785df930be7Sderaadt 	old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK);
786df930be7Sderaadt 
787df930be7Sderaadt 	new_segnum = new_bucket >> hashp->SSHIFT;
788df930be7Sderaadt 
789df930be7Sderaadt 	/* Check if we need a new segment */
790df930be7Sderaadt 	if (new_segnum >= hashp->nsegs) {
791df930be7Sderaadt 		/* Check if we need to expand directory */
792df930be7Sderaadt 		if (new_segnum >= hashp->DSIZE) {
793df930be7Sderaadt 			/* Reallocate directory */
794df930be7Sderaadt 			dirsize = hashp->DSIZE * sizeof(SEGMENT *);
795df930be7Sderaadt 			if (!hash_realloc(&hashp->dir, dirsize, dirsize << 1))
796df930be7Sderaadt 				return (-1);
797df930be7Sderaadt 			hashp->DSIZE = dirsize << 1;
798df930be7Sderaadt 		}
799df930be7Sderaadt 		if ((hashp->dir[new_segnum] =
800df930be7Sderaadt 		    (SEGMENT)calloc(hashp->SGSIZE, sizeof(SEGMENT))) == NULL)
801df930be7Sderaadt 			return (-1);
802df930be7Sderaadt 		hashp->exsegs++;
803df930be7Sderaadt 		hashp->nsegs++;
804df930be7Sderaadt 	}
805df930be7Sderaadt 	/*
806df930be7Sderaadt 	 * If the split point is increasing (MAX_BUCKET's log base 2
807df930be7Sderaadt 	 * * increases), we need to copy the current contents of the spare
808df930be7Sderaadt 	 * split bucket to the next bucket.
809df930be7Sderaadt 	 */
810df930be7Sderaadt 	spare_ndx = __log2(hashp->MAX_BUCKET + 1);
811df930be7Sderaadt 	if (spare_ndx > hashp->OVFL_POINT) {
812df930be7Sderaadt 		hashp->SPARES[spare_ndx] = hashp->SPARES[hashp->OVFL_POINT];
813df930be7Sderaadt 		hashp->OVFL_POINT = spare_ndx;
814df930be7Sderaadt 	}
815df930be7Sderaadt 
816df930be7Sderaadt 	if (new_bucket > hashp->HIGH_MASK) {
817df930be7Sderaadt 		/* Starting a new doubling */
818df930be7Sderaadt 		hashp->LOW_MASK = hashp->HIGH_MASK;
819df930be7Sderaadt 		hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK;
820df930be7Sderaadt 	}
821df930be7Sderaadt 	/* Relocate records to the new bucket */
822df930be7Sderaadt 	return (__split_page(hashp, old_bucket, new_bucket));
823df930be7Sderaadt }
824df930be7Sderaadt 
825df930be7Sderaadt /*
826df930be7Sderaadt  * If realloc guarantees that the pointer is not destroyed if the realloc
827df930be7Sderaadt  * fails, then this routine can go away.
828df930be7Sderaadt  */
829df930be7Sderaadt static void *
hash_realloc(SEGMENT ** p_ptr,int oldsize,int newsize)830e20a56a5Sotto hash_realloc(SEGMENT **p_ptr, int oldsize, int newsize)
831df930be7Sderaadt {
8326c51c909Smillert 	void *p;
833df930be7Sderaadt 
834dfa5a4f6Smillert 	if ((p = malloc(newsize))) {
835df930be7Sderaadt 		memmove(p, *p_ptr, oldsize);
836df930be7Sderaadt 		memset((char *)p + oldsize, 0, newsize - oldsize);
837df930be7Sderaadt 		free(*p_ptr);
838df930be7Sderaadt 		*p_ptr = p;
839df930be7Sderaadt 	}
840df930be7Sderaadt 	return (p);
841df930be7Sderaadt }
842df930be7Sderaadt 
843e20a56a5Sotto u_int32_t
__call_hash(HTAB * hashp,char * k,int len)844e20a56a5Sotto __call_hash(HTAB *hashp, char *k, int len)
845df930be7Sderaadt {
846df930be7Sderaadt 	int n, bucket;
847df930be7Sderaadt 
848df930be7Sderaadt 	n = hashp->hash(k, len);
849df930be7Sderaadt 	bucket = n & hashp->HIGH_MASK;
850df930be7Sderaadt 	if (bucket > hashp->MAX_BUCKET)
851df930be7Sderaadt 		bucket = bucket & hashp->LOW_MASK;
852df930be7Sderaadt 	return (bucket);
853df930be7Sderaadt }
854df930be7Sderaadt 
855df930be7Sderaadt /*
856df930be7Sderaadt  * Allocate segment table.  On error, destroy the table and set errno.
857df930be7Sderaadt  *
858df930be7Sderaadt  * Returns 0 on success
859df930be7Sderaadt  */
860df930be7Sderaadt static int
alloc_segs(HTAB * hashp,int nsegs)861e20a56a5Sotto alloc_segs(HTAB *hashp, int nsegs)
862df930be7Sderaadt {
8636c51c909Smillert 	int i;
8646c51c909Smillert 	SEGMENT store;
865df930be7Sderaadt 
866df930be7Sderaadt 	int save_errno;
867df930be7Sderaadt 
868df930be7Sderaadt 	if ((hashp->dir =
869df930be7Sderaadt 	    (SEGMENT *)calloc(hashp->DSIZE, sizeof(SEGMENT *))) == NULL) {
870df930be7Sderaadt 		save_errno = errno;
871df930be7Sderaadt 		(void)hdestroy(hashp);
872df930be7Sderaadt 		errno = save_errno;
873df930be7Sderaadt 		return (-1);
874df930be7Sderaadt 	}
87507f46334Sotto 	hashp->nsegs = nsegs;
87607f46334Sotto 	if (nsegs == 0)
87707f46334Sotto 		return (0);
878df930be7Sderaadt 	/* Allocate segments */
87907f46334Sotto 	if ((store = (SEGMENT)calloc(nsegs << hashp->SSHIFT,
88007f46334Sotto 	    sizeof(SEGMENT))) == NULL) {
881df930be7Sderaadt 		save_errno = errno;
882df930be7Sderaadt 		(void)hdestroy(hashp);
883df930be7Sderaadt 		errno = save_errno;
884df930be7Sderaadt 		return (-1);
885df930be7Sderaadt 	}
88607f46334Sotto 	for (i = 0; i < nsegs; i++)
887df930be7Sderaadt 		hashp->dir[i] = &store[i << hashp->SSHIFT];
888df930be7Sderaadt 	return (0);
889df930be7Sderaadt }
890df930be7Sderaadt 
891df930be7Sderaadt #if BYTE_ORDER == LITTLE_ENDIAN
892df930be7Sderaadt /*
893df930be7Sderaadt  * Hashp->hdr needs to be byteswapped.
894df930be7Sderaadt  */
895df930be7Sderaadt static void
swap_header_copy(HASHHDR * srcp,HASHHDR * destp)896e20a56a5Sotto swap_header_copy(HASHHDR *srcp, HASHHDR *destp)
897df930be7Sderaadt {
898df930be7Sderaadt 	int i;
899df930be7Sderaadt 
900df930be7Sderaadt 	P_32_COPY(srcp->magic, destp->magic);
901df930be7Sderaadt 	P_32_COPY(srcp->version, destp->version);
902df930be7Sderaadt 	P_32_COPY(srcp->lorder, destp->lorder);
903df930be7Sderaadt 	P_32_COPY(srcp->bsize, destp->bsize);
904df930be7Sderaadt 	P_32_COPY(srcp->bshift, destp->bshift);
905df930be7Sderaadt 	P_32_COPY(srcp->dsize, destp->dsize);
906df930be7Sderaadt 	P_32_COPY(srcp->ssize, destp->ssize);
907df930be7Sderaadt 	P_32_COPY(srcp->sshift, destp->sshift);
908df930be7Sderaadt 	P_32_COPY(srcp->ovfl_point, destp->ovfl_point);
909df930be7Sderaadt 	P_32_COPY(srcp->last_freed, destp->last_freed);
910df930be7Sderaadt 	P_32_COPY(srcp->max_bucket, destp->max_bucket);
911df930be7Sderaadt 	P_32_COPY(srcp->high_mask, destp->high_mask);
912df930be7Sderaadt 	P_32_COPY(srcp->low_mask, destp->low_mask);
913df930be7Sderaadt 	P_32_COPY(srcp->ffactor, destp->ffactor);
914df930be7Sderaadt 	P_32_COPY(srcp->nkeys, destp->nkeys);
915df930be7Sderaadt 	P_32_COPY(srcp->hdrpages, destp->hdrpages);
916df930be7Sderaadt 	P_32_COPY(srcp->h_charkey, destp->h_charkey);
917df930be7Sderaadt 	for (i = 0; i < NCACHED; i++) {
918df930be7Sderaadt 		P_32_COPY(srcp->spares[i], destp->spares[i]);
919df930be7Sderaadt 		P_16_COPY(srcp->bitmaps[i], destp->bitmaps[i]);
920df930be7Sderaadt 	}
921df930be7Sderaadt }
922df930be7Sderaadt 
923df930be7Sderaadt static void
swap_header(HTAB * hashp)924e20a56a5Sotto swap_header(HTAB *hashp)
925df930be7Sderaadt {
926df930be7Sderaadt 	HASHHDR *hdrp;
927df930be7Sderaadt 	int i;
928df930be7Sderaadt 
929df930be7Sderaadt 	hdrp = &hashp->hdr;
930df930be7Sderaadt 
931df930be7Sderaadt 	M_32_SWAP(hdrp->magic);
932df930be7Sderaadt 	M_32_SWAP(hdrp->version);
933df930be7Sderaadt 	M_32_SWAP(hdrp->lorder);
934df930be7Sderaadt 	M_32_SWAP(hdrp->bsize);
935df930be7Sderaadt 	M_32_SWAP(hdrp->bshift);
936df930be7Sderaadt 	M_32_SWAP(hdrp->dsize);
937df930be7Sderaadt 	M_32_SWAP(hdrp->ssize);
938df930be7Sderaadt 	M_32_SWAP(hdrp->sshift);
939df930be7Sderaadt 	M_32_SWAP(hdrp->ovfl_point);
940df930be7Sderaadt 	M_32_SWAP(hdrp->last_freed);
941df930be7Sderaadt 	M_32_SWAP(hdrp->max_bucket);
942df930be7Sderaadt 	M_32_SWAP(hdrp->high_mask);
943df930be7Sderaadt 	M_32_SWAP(hdrp->low_mask);
944df930be7Sderaadt 	M_32_SWAP(hdrp->ffactor);
945df930be7Sderaadt 	M_32_SWAP(hdrp->nkeys);
946df930be7Sderaadt 	M_32_SWAP(hdrp->hdrpages);
947df930be7Sderaadt 	M_32_SWAP(hdrp->h_charkey);
948df930be7Sderaadt 	for (i = 0; i < NCACHED; i++) {
949df930be7Sderaadt 		M_32_SWAP(hdrp->spares[i]);
950df930be7Sderaadt 		M_16_SWAP(hdrp->bitmaps[i]);
951df930be7Sderaadt 	}
952df930be7Sderaadt }
953df930be7Sderaadt #endif
954