146366Sbostic /*-
262489Sbostic * Copyright (c) 1990, 1993
362489Sbostic * The Regents of the University of California. 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*66215Sbostic static char sccsid[] = "@(#)hash.c 8.7 (Berkeley) 02/21/94";
1346366Sbostic #endif /* LIBC_SCCS and not lint */
1446366Sbostic
1546366Sbostic #include <sys/param.h>
1646366Sbostic #include <sys/stat.h>
1755452Sbostic
1857586Sbostic #include <errno.h>
1946562Sbostic #include <fcntl.h>
2057586Sbostic #include <stdio.h>
2157586Sbostic #include <stdlib.h>
2257586Sbostic #include <string.h>
2357586Sbostic #include <unistd.h>
2450997Sbostic #ifdef DEBUG
2546366Sbostic #include <assert.h>
2650997Sbostic #endif
2757586Sbostic
2857932Sbostic #include <db.h>
2946366Sbostic #include "hash.h"
3050997Sbostic #include "page.h"
3150997Sbostic #include "extern.h"
3246366Sbostic
3357586Sbostic static int alloc_segs __P((HTAB *, int));
3457586Sbostic static int flush_meta __P((HTAB *));
3557586Sbostic static int hash_access __P((HTAB *, ACTION, DBT *, DBT *));
3650997Sbostic static int hash_close __P((DB *));
3750997Sbostic static int hash_delete __P((const DB *, const DBT *, u_int));
3860247Sbostic static int hash_fd __P((const DB *));
3951057Sbostic static int hash_get __P((const DB *, const DBT *, DBT *, u_int));
4056893Sbostic static int hash_put __P((const DB *, DBT *, const DBT *, u_int));
4150997Sbostic static void *hash_realloc __P((SEGMENT **, int, int));
4250997Sbostic static int hash_seq __P((const DB *, DBT *, DBT *, u_int));
4360060Sbostic static int hash_sync __P((const DB *, u_int));
4457586Sbostic static int hdestroy __P((HTAB *));
4560247Sbostic static HTAB *init_hash __P((HTAB *, const char *, HASHINFO *));
4657586Sbostic static int init_htab __P((HTAB *, int));
4750997Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
4858127Sralph static void swap_header __P((HTAB *));
4950997Sbostic static void swap_header_copy __P((HASHHDR *, HASHHDR *));
5050997Sbostic #endif
5150997Sbostic
5246366Sbostic /* Fast arithmetic, relying on powers of 2, */
5350997Sbostic #define MOD(x, y) ((x) & ((y) - 1))
5446366Sbostic
5550997Sbostic #define RETURN_ERROR(ERR, LOC) { save_errno = ERR; goto LOC; }
5646366Sbostic
5746366Sbostic /* Return values */
5850997Sbostic #define SUCCESS (0)
5950997Sbostic #define ERROR (-1)
6050997Sbostic #define ABNORMAL (1)
6146366Sbostic
6246366Sbostic #ifdef HASH_STATISTICS
6346366Sbostic long hash_accesses, hash_collisions, hash_expansions, hash_overflows;
6446366Sbostic #endif
6546366Sbostic
6650997Sbostic /************************** INTERFACE ROUTINES ***************************/
6746366Sbostic /* OPEN/CLOSE */
6846366Sbostic
6950997Sbostic extern DB *
7064466Sbostic __hash_open(file, flags, mode, info, dflags)
7150997Sbostic const char *file;
7264466Sbostic int flags, mode, dflags;
7350997Sbostic const HASHINFO *info; /* Special directives for create */
7446366Sbostic {
7557586Sbostic HTAB *hashp;
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
85*66215Sbostic if (!(hashp = (HTAB *)calloc(1, sizeof(HTAB))))
8650997Sbostic return (NULL);
8750997Sbostic hashp->fp = -1;
8864466Sbostic
8950997Sbostic /*
9064466Sbostic * Even if user wants write only, we need to be able to read
9164466Sbostic * the actual file, so we need to open it read/write. But, the
9264466Sbostic * field in the hashp structure needs to be accurate so that
9350997Sbostic * we can check accesses.
9450997Sbostic */
9564466Sbostic hashp->flags = flags;
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) {
11060247Sbostic if (!(hashp = init_hash(hashp, file, (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
11757586Sbostic hashp->hash = __default_hash;
11846366Sbostic
11950997Sbostic hdrsize = read(hashp->fp, &hashp->hdr, sizeof(HASHHDR));
12046366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
12157586Sbostic swap_header(hashp);
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);
13064708Sbostic #define OLDHASHVERSION 1
13164708Sbostic if (hashp->VERSION != HASHVERSION &&
13264708Sbostic hashp->VERSION != OLDHASHVERSION)
13350997Sbostic RETURN_ERROR(EFTYPE, error1);
13450997Sbostic if (hashp->hash(CHARKEY, sizeof(CHARKEY)) != hashp->H_CHARKEY)
13550997Sbostic RETURN_ERROR(EFTYPE, error1);
13651057Sbostic /*
13751057Sbostic * Figure out how many segments we need. Max_Bucket is the
13851057Sbostic * maximum bucket number, so the number of buckets is
13951057Sbostic * max_bucket + 1.
14051057Sbostic */
14151057Sbostic nsegs = (hashp->MAX_BUCKET + 1 + hashp->SGSIZE - 1) /
14250997Sbostic hashp->SGSIZE;
14350997Sbostic hashp->nsegs = 0;
14457586Sbostic if (alloc_segs(hashp, nsegs))
14550997Sbostic /*
14650997Sbostic * If alloc_segs fails, table will have been destroyed
14750997Sbostic * and errno will have been set.
14850997Sbostic */
14950997Sbostic return (NULL);
15050997Sbostic /* Read in bitmaps */
15151061Sbostic bpages = (hashp->SPARES[hashp->OVFL_POINT] +
15250997Sbostic (hashp->BSIZE << BYTE_SHIFT) - 1) >>
15350997Sbostic (hashp->BSHIFT + BYTE_SHIFT);
15446366Sbostic
15550997Sbostic hashp->nmaps = bpages;
15651057Sbostic (void)memset(&hashp->mapp[0], 0, bpages * sizeof(u_long *));
15746366Sbostic }
15846366Sbostic
15950997Sbostic /* Initialize Buffer Manager */
16050997Sbostic if (info && info->cachesize)
16157586Sbostic __buf_init(hashp, info->cachesize);
16250997Sbostic else
16357586Sbostic __buf_init(hashp, DEF_BUFSIZE);
16450997Sbostic
16550997Sbostic hashp->new_file = new_table;
16656422Smargo hashp->save_file = file && (hashp->flags & O_RDWR);
16750997Sbostic hashp->cbucket = -1;
168*66215Sbostic if (!(dbp = (DB *)malloc(sizeof(DB)))) {
16950997Sbostic save_errno = errno;
17057586Sbostic hdestroy(hashp);
17150997Sbostic errno = save_errno;
17250997Sbostic return (NULL);
17346366Sbostic }
17457586Sbostic dbp->internal = hashp;
17550997Sbostic dbp->close = hash_close;
17650997Sbostic dbp->del = hash_delete;
17760247Sbostic dbp->fd = hash_fd;
17850997Sbostic dbp->get = hash_get;
17950997Sbostic dbp->put = hash_put;
18050997Sbostic dbp->seq = hash_seq;
18150997Sbostic dbp->sync = hash_sync;
18250997Sbostic dbp->type = DB_HASH;
18346366Sbostic
18446366Sbostic #ifdef DEBUG
18550997Sbostic (void)fprintf(stderr,
18651061Sbostic "%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",
18750997Sbostic "init_htab:",
18850997Sbostic "TABLE POINTER ", hashp,
18950997Sbostic "BUCKET SIZE ", hashp->BSIZE,
19050997Sbostic "BUCKET SHIFT ", hashp->BSHIFT,
19150997Sbostic "DIRECTORY SIZE ", hashp->DSIZE,
19250997Sbostic "SEGMENT SIZE ", hashp->SGSIZE,
19350997Sbostic "SEGMENT SHIFT ", hashp->SSHIFT,
19450997Sbostic "FILL FACTOR ", hashp->FFACTOR,
19550997Sbostic "MAX BUCKET ", hashp->MAX_BUCKET,
19651061Sbostic "OVFL POINT ", hashp->OVFL_POINT,
19751061Sbostic "LAST FREED ", hashp->LAST_FREED,
19850997Sbostic "HIGH MASK ", hashp->HIGH_MASK,
19950997Sbostic "LOW MASK ", hashp->LOW_MASK,
20050997Sbostic "NSEGS ", hashp->nsegs,
20150997Sbostic "NKEYS ", hashp->NKEYS);
20246366Sbostic #endif
20346366Sbostic #ifdef HASH_STATISTICS
20446366Sbostic hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0;
20546366Sbostic #endif
20650997Sbostic return (dbp);
20746366Sbostic
20846366Sbostic error1:
20951192Sbostic if (hashp != NULL)
21051192Sbostic (void)close(hashp->fp);
21146366Sbostic
21246366Sbostic error0:
21350997Sbostic free(hashp);
21450997Sbostic errno = save_errno;
21550997Sbostic return (NULL);
21646366Sbostic }
21746366Sbostic
21846366Sbostic static int
hash_close(dbp)21950997Sbostic hash_close(dbp)
22050997Sbostic DB *dbp;
22146366Sbostic {
22257586Sbostic HTAB *hashp;
22350997Sbostic int retval;
22446457Sbostic
22550997Sbostic if (!dbp)
22650997Sbostic return (ERROR);
22757586Sbostic
22846366Sbostic hashp = (HTAB *)dbp->internal;
22957586Sbostic retval = hdestroy(hashp);
23050997Sbostic free(dbp);
23150997Sbostic return (retval);
23246366Sbostic }
23346366Sbostic
23460247Sbostic static int
hash_fd(dbp)23560247Sbostic hash_fd(dbp)
23660247Sbostic const DB *dbp;
23760247Sbostic {
23860247Sbostic HTAB *hashp;
23960247Sbostic
24060247Sbostic if (!dbp)
24160247Sbostic return (ERROR);
24260247Sbostic
24360247Sbostic hashp = (HTAB *)dbp->internal;
24460247Sbostic if (hashp->fp == -1) {
24560247Sbostic errno = ENOENT;
24660247Sbostic return (-1);
24760247Sbostic }
24860247Sbostic return (hashp->fp);
24960247Sbostic }
25060247Sbostic
25146366Sbostic /************************** LOCAL CREATION ROUTINES **********************/
25250997Sbostic static HTAB *
init_hash(hashp,file,info)25360247Sbostic init_hash(hashp, file, info)
25457586Sbostic HTAB *hashp;
25560247Sbostic const char *file;
25650997Sbostic HASHINFO *info;
25746366Sbostic {
25860247Sbostic struct stat statbuf;
25950997Sbostic int nelem;
26046366Sbostic
26146366Sbostic nelem = 1;
26251061Sbostic hashp->NKEYS = 0;
26350997Sbostic hashp->LORDER = BYTE_ORDER;
26450997Sbostic hashp->BSIZE = DEF_BUCKET_SIZE;
26550997Sbostic hashp->BSHIFT = DEF_BUCKET_SHIFT;
26650997Sbostic hashp->SGSIZE = DEF_SEGSIZE;
26750997Sbostic hashp->SSHIFT = DEF_SEGSIZE_SHIFT;
26850997Sbostic hashp->DSIZE = DEF_DIRSIZE;
26950997Sbostic hashp->FFACTOR = DEF_FFACTOR;
27057586Sbostic hashp->hash = __default_hash;
27158016Sbostic memset(hashp->SPARES, 0, sizeof(hashp->SPARES));
27258016Sbostic memset(hashp->BITMAPS, 0, sizeof (hashp->BITMAPS));
27346366Sbostic
27460247Sbostic /* Fix bucket size to be optimal for file system */
27560247Sbostic if (file != NULL) {
27660247Sbostic if (stat(file, &statbuf))
27760247Sbostic return (NULL);
27860247Sbostic hashp->BSIZE = statbuf.st_blksize;
27960247Sbostic hashp->BSHIFT = __log2(hashp->BSIZE);
28060247Sbostic }
28160247Sbostic
28250997Sbostic if (info) {
28350997Sbostic if (info->bsize) {
28450997Sbostic /* Round pagesize up to power of 2 */
28550997Sbostic hashp->BSHIFT = __log2(info->bsize);
28650997Sbostic hashp->BSIZE = 1 << hashp->BSHIFT;
28750997Sbostic if (hashp->BSIZE > MAX_BSIZE) {
28850997Sbostic errno = EINVAL;
28950997Sbostic return (NULL);
29050997Sbostic }
29146366Sbostic }
29250997Sbostic if (info->ffactor)
29350997Sbostic hashp->FFACTOR = info->ffactor;
29450997Sbostic if (info->hash)
29550997Sbostic hashp->hash = info->hash;
29650997Sbostic if (info->nelem)
29750997Sbostic nelem = info->nelem;
29850997Sbostic if (info->lorder) {
29950997Sbostic if (info->lorder != BIG_ENDIAN &&
30050997Sbostic info->lorder != LITTLE_ENDIAN) {
30150997Sbostic errno = EINVAL;
30250997Sbostic return (NULL);
30350997Sbostic }
30450997Sbostic hashp->LORDER = info->lorder;
30546366Sbostic }
30646366Sbostic }
30746366Sbostic /* init_htab should destroy the table and set errno if it fails */
30857586Sbostic if (init_htab(hashp, nelem))
30950997Sbostic return (NULL);
31050997Sbostic else
31150997Sbostic return (hashp);
31246366Sbostic }
31346366Sbostic /*
31450997Sbostic * This calls alloc_segs which may run out of memory. Alloc_segs will destroy
31550997Sbostic * the table and set errno, so we just pass the error information along.
31650997Sbostic *
31750997Sbostic * Returns 0 on No Error
31850997Sbostic */
31946366Sbostic static int
init_htab(hashp,nelem)32057586Sbostic init_htab(hashp, nelem)
32157586Sbostic HTAB *hashp;
32250997Sbostic int nelem;
32346366Sbostic {
32450997Sbostic register int nbuckets, nsegs;
32550997Sbostic int l2;
32646366Sbostic
32750997Sbostic /*
32850997Sbostic * Divide number of elements by the fill factor and determine a
32950997Sbostic * desired number of buckets. Allocate space for the next greater
33050997Sbostic * power of two number of buckets.
33150997Sbostic */
33246366Sbostic nelem = (nelem - 1) / hashp->FFACTOR + 1;
33346366Sbostic
33452001Sbostic l2 = __log2(MAX(nelem, 2));
33546366Sbostic nbuckets = 1 << l2;
33646366Sbostic
33746366Sbostic hashp->SPARES[l2] = l2 + 1;
33850997Sbostic hashp->SPARES[l2 + 1] = l2 + 1;
33951061Sbostic hashp->OVFL_POINT = l2;
34051061Sbostic hashp->LAST_FREED = 2;
34151061Sbostic
34250997Sbostic /* First bitmap page is at: splitpoint l2 page offset 1 */
34357586Sbostic if (__init_bitmap(hashp, OADDR_OF(l2, 1), l2 + 1, 0))
34451057Sbostic return (-1);
34546366Sbostic
34646366Sbostic hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1;
34746366Sbostic hashp->HIGH_MASK = (nbuckets << 1) - 1;
34850997Sbostic hashp->HDRPAGES = ((MAX(sizeof(HASHHDR), MINHDRSIZE) - 1) >>
34950997Sbostic hashp->BSHIFT) + 1;
35046366Sbostic
35146366Sbostic nsegs = (nbuckets - 1) / hashp->SGSIZE + 1;
35246366Sbostic nsegs = 1 << __log2(nsegs);
35346366Sbostic
35450997Sbostic if (nsegs > hashp->DSIZE)
35550997Sbostic hashp->DSIZE = nsegs;
35657586Sbostic return (alloc_segs(hashp, nsegs));
35746366Sbostic }
35846366Sbostic
35946366Sbostic /********************** DESTROY/CLOSE ROUTINES ************************/
36046366Sbostic
36146366Sbostic /*
36250997Sbostic * Flushes any changes to the file if necessary and destroys the hashp
36350997Sbostic * structure, freeing all allocated space.
36450997Sbostic */
36546366Sbostic static int
hdestroy(hashp)36657586Sbostic hdestroy(hashp)
36757586Sbostic HTAB *hashp;
36846366Sbostic {
36950997Sbostic int i, save_errno;
37046366Sbostic
37146366Sbostic save_errno = 0;
37246366Sbostic
37346366Sbostic #ifdef HASH_STATISTICS
37457586Sbostic (void)fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n",
37557586Sbostic hash_accesses, hash_collisions);
37657586Sbostic (void)fprintf(stderr, "hdestroy: expansions %ld\n",
37757586Sbostic hash_expansions);
37857586Sbostic (void)fprintf(stderr, "hdestroy: overflows %ld\n",
37957586Sbostic hash_overflows);
38057586Sbostic (void)fprintf(stderr, "keys %ld maxp %d segmentcount %d\n",
38157586Sbostic hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs);
38246366Sbostic
38357586Sbostic for (i = 0; i < NCACHED; i++)
38457586Sbostic (void)fprintf(stderr,
38557586Sbostic "spares[%d] = %d\n", i, hashp->SPARES[i]);
38646366Sbostic #endif
38757586Sbostic /*
38857586Sbostic * Call on buffer manager to free buffers, and if required,
38957586Sbostic * write them to disk.
39057586Sbostic */
39157586Sbostic if (__buf_free(hashp, 1, hashp->save_file))
39257586Sbostic save_errno = errno;
39357586Sbostic if (hashp->dir) {
39457586Sbostic free(*hashp->dir); /* Free initial segments */
39557586Sbostic /* Free extra segments */
39657586Sbostic while (hashp->exsegs--)
39757586Sbostic free(hashp->dir[--hashp->nsegs]);
39857586Sbostic free(hashp->dir);
39957586Sbostic }
40057586Sbostic if (flush_meta(hashp) && !save_errno)
40157586Sbostic save_errno = errno;
40257586Sbostic /* Free Bigmaps */
40357586Sbostic for (i = 0; i < hashp->nmaps; i++)
40457586Sbostic if (hashp->mapp[i])
40557586Sbostic free(hashp->mapp[i]);
40646503Sbostic
40757586Sbostic if (hashp->fp != -1)
40857586Sbostic (void)close(hashp->fp);
40957586Sbostic
41065725Sbostic free(hashp);
41165725Sbostic
41250997Sbostic if (save_errno) {
41350997Sbostic errno = save_errno;
41450997Sbostic return (ERROR);
41546366Sbostic }
41650997Sbostic return (SUCCESS);
41746366Sbostic }
41850997Sbostic /*
41950997Sbostic * Write modified pages to disk
42050997Sbostic *
42150997Sbostic * Returns:
42250997Sbostic * 0 == OK
42350997Sbostic * -1 ERROR
42450997Sbostic */
42546366Sbostic static int
hash_sync(dbp,flags)42660060Sbostic hash_sync(dbp, flags)
42750997Sbostic const DB *dbp;
42860060Sbostic u_int flags;
42946366Sbostic {
43057586Sbostic HTAB *hashp;
43157586Sbostic
43260060Sbostic if (flags != 0) {
43360060Sbostic errno = EINVAL;
43460060Sbostic return (ERROR);
43560060Sbostic }
43660060Sbostic
43750997Sbostic if (!dbp)
43850997Sbostic return (ERROR);
43957586Sbostic
44046366Sbostic hashp = (HTAB *)dbp->internal;
44150997Sbostic if (!hashp->save_file)
44250997Sbostic return (0);
44357586Sbostic if (__buf_free(hashp, 0, 1) || flush_meta(hashp))
44450997Sbostic return (ERROR);
44546366Sbostic hashp->new_file = 0;
44650997Sbostic return (0);
44746366Sbostic }
44846366Sbostic
44946366Sbostic /*
45050997Sbostic * Returns:
45150997Sbostic * 0 == OK
45250997Sbostic * -1 indicates that errno should be set
45350997Sbostic */
45446366Sbostic static int
flush_meta(hashp)45557586Sbostic flush_meta(hashp)
45657586Sbostic HTAB *hashp;
45746366Sbostic {
45851814Smarc HASHHDR *whdrp;
45951814Smarc #if BYTE_ORDER == LITTLE_ENDIAN
46051814Smarc HASHHDR whdr;
46151814Smarc #endif
46250997Sbostic int fp, i, wsize;
46346366Sbostic
46450997Sbostic if (!hashp->save_file)
46550997Sbostic return (0);
46646366Sbostic hashp->MAGIC = HASHMAGIC;
46751061Sbostic hashp->VERSION = HASHVERSION;
46850997Sbostic hashp->H_CHARKEY = hashp->hash(CHARKEY, sizeof(CHARKEY));
46946366Sbostic
47046366Sbostic fp = hashp->fp;
47146366Sbostic whdrp = &hashp->hdr;
47246366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
47346366Sbostic whdrp = &whdr;
47450997Sbostic swap_header_copy(&hashp->hdr, whdrp);
47546366Sbostic #endif
47656669Sbostic if ((lseek(fp, (off_t)0, SEEK_SET) == -1) ||
47750997Sbostic ((wsize = write(fp, whdrp, sizeof(HASHHDR))) == -1))
47850997Sbostic return (-1);
47950997Sbostic else
48050997Sbostic if (wsize != sizeof(HASHHDR)) {
48150997Sbostic errno = EFTYPE;
48250997Sbostic hashp->errno = errno;
48350997Sbostic return (-1);
48446366Sbostic }
48550997Sbostic for (i = 0; i < NCACHED; i++)
48650997Sbostic if (hashp->mapp[i])
48757586Sbostic if (__put_page(hashp, (char *)hashp->mapp[i],
48850997Sbostic hashp->BITMAPS[i], 0, 1))
48950997Sbostic return (-1);
49050997Sbostic return (0);
49146366Sbostic }
49250997Sbostic
49346366Sbostic /*******************************SEARCH ROUTINES *****************************/
49446366Sbostic /*
49550997Sbostic * All the access routines return
49650997Sbostic *
49750997Sbostic * Returns:
49850997Sbostic * 0 on SUCCESS
49950997Sbostic * 1 to indicate an external ERROR (i.e. key not found, etc)
50050997Sbostic * -1 to indicate an internal ERROR (i.e. out of memory, etc)
50150997Sbostic */
50246366Sbostic static int
hash_get(dbp,key,data,flag)50350997Sbostic hash_get(dbp, key, data, flag)
50450997Sbostic const DB *dbp;
50551057Sbostic const DBT *key;
50651057Sbostic DBT *data;
50750997Sbostic u_int flag;
50846366Sbostic {
50957586Sbostic HTAB *hashp;
51057586Sbostic
51157586Sbostic hashp = (HTAB *)dbp->internal;
51250997Sbostic if (flag) {
51350997Sbostic hashp->errno = errno = EINVAL;
51450997Sbostic return (ERROR);
51550997Sbostic }
51657586Sbostic return (hash_access(hashp, HASH_GET, (DBT *)key, data));
51746366Sbostic }
51846366Sbostic
51946366Sbostic static int
hash_put(dbp,key,data,flag)52050997Sbostic hash_put(dbp, key, data, flag)
52150997Sbostic const DB *dbp;
52256893Sbostic DBT *key;
52356893Sbostic const DBT *data;
52450997Sbostic u_int flag;
52546366Sbostic {
52657586Sbostic HTAB *hashp;
52757586Sbostic
52857586Sbostic hashp = (HTAB *)dbp->internal;
52950997Sbostic if (flag && flag != R_NOOVERWRITE) {
53050997Sbostic hashp->errno = errno = EINVAL;
53150997Sbostic return (ERROR);
53250997Sbostic }
53350997Sbostic if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
53450997Sbostic hashp->errno = errno = EPERM;
53550997Sbostic return (ERROR);
53650997Sbostic }
53757586Sbostic return (hash_access(hashp, flag == R_NOOVERWRITE ?
53850997Sbostic HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data));
53946366Sbostic }
54046366Sbostic
54146366Sbostic static int
hash_delete(dbp,key,flag)54250997Sbostic hash_delete(dbp, key, flag)
54350997Sbostic const DB *dbp;
54450997Sbostic const DBT *key;
54550997Sbostic u_int flag; /* Ignored */
54646366Sbostic {
54757586Sbostic HTAB *hashp;
54857586Sbostic
54957586Sbostic hashp = (HTAB *)dbp->internal;
55050997Sbostic if (flag && flag != R_CURSOR) {
55150997Sbostic hashp->errno = errno = EINVAL;
55250997Sbostic return (ERROR);
55350997Sbostic }
55450997Sbostic if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
55550997Sbostic hashp->errno = errno = EPERM;
55650997Sbostic return (ERROR);
55750997Sbostic }
55857586Sbostic return (hash_access(hashp, HASH_DELETE, (DBT *)key, NULL));
55946366Sbostic }
56046366Sbostic
56146366Sbostic /*
56250997Sbostic * Assume that hashp has been set in wrapper routine.
56350997Sbostic */
56446366Sbostic static int
hash_access(hashp,action,key,val)56557586Sbostic hash_access(hashp, action, key, val)
56657586Sbostic HTAB *hashp;
56750997Sbostic ACTION action;
56850997Sbostic DBT *key, *val;
56946366Sbostic {
57050997Sbostic register BUFHEAD *rbufp;
57150997Sbostic BUFHEAD *bufp, *save_bufp;
57250997Sbostic register u_short *bp;
57350997Sbostic register int n, ndx, off, size;
57450997Sbostic register char *kp;
57550997Sbostic u_short pageno;
57646366Sbostic
57746366Sbostic #ifdef HASH_STATISTICS
57846366Sbostic hash_accesses++;
57946366Sbostic #endif
58046366Sbostic
58150997Sbostic off = hashp->BSIZE;
58246366Sbostic size = key->size;
58346950Sbostic kp = (char *)key->data;
58457586Sbostic rbufp = __get_buf(hashp, __call_hash(hashp, kp, size), NULL, 0);
58550997Sbostic if (!rbufp)
58650997Sbostic return (ERROR);
58746457Sbostic save_bufp = rbufp;
58846366Sbostic
58946457Sbostic /* Pin the bucket chain */
59046457Sbostic rbufp->flags |= BUF_PIN;
59150997Sbostic for (bp = (u_short *)rbufp->page, n = *bp++, ndx = 1; ndx < n;)
59250997Sbostic if (bp[1] >= REAL_KEY) {
59350997Sbostic /* Real key/data pair */
59450997Sbostic if (size == off - *bp &&
59558016Sbostic memcmp(kp, rbufp->page + *bp, size) == 0)
59650997Sbostic goto found;
59750997Sbostic off = bp[1];
59846366Sbostic #ifdef HASH_STATISTICS
59950997Sbostic hash_collisions++;
60046366Sbostic #endif
60150997Sbostic bp += 2;
60250997Sbostic ndx += 2;
60350997Sbostic } else if (bp[1] == OVFLPAGE) {
60457586Sbostic rbufp = __get_buf(hashp, *bp, rbufp, 0);
60550997Sbostic if (!rbufp) {
60650997Sbostic save_bufp->flags &= ~BUF_PIN;
60750997Sbostic return (ERROR);
60850997Sbostic }
60950997Sbostic /* FOR LOOP INIT */
61050997Sbostic bp = (u_short *)rbufp->page;
61150997Sbostic n = *bp++;
61250997Sbostic ndx = 1;
61350997Sbostic off = hashp->BSIZE;
61450997Sbostic } else if (bp[1] < REAL_KEY) {
61557586Sbostic if ((ndx =
61657586Sbostic __find_bigpair(hashp, rbufp, ndx, kp, size)) > 0)
61750997Sbostic goto found;
61850997Sbostic if (ndx == -2) {
61950997Sbostic bufp = rbufp;
62057586Sbostic if (!(pageno =
62157586Sbostic __find_last_page(hashp, &bufp))) {
62250997Sbostic ndx = 0;
62350997Sbostic rbufp = bufp;
62450997Sbostic break; /* FOR */
62550997Sbostic }
62657586Sbostic rbufp = __get_buf(hashp, pageno, bufp, 0);
62750997Sbostic if (!rbufp) {
62850997Sbostic save_bufp->flags &= ~BUF_PIN;
62950997Sbostic return (ERROR);
63050997Sbostic }
63150997Sbostic /* FOR LOOP INIT */
63250997Sbostic bp = (u_short *)rbufp->page;
63350997Sbostic n = *bp++;
63450997Sbostic ndx = 1;
63550997Sbostic off = hashp->BSIZE;
63650997Sbostic } else {
63750997Sbostic save_bufp->flags &= ~BUF_PIN;
63850997Sbostic return (ERROR);
63950997Sbostic }
64046457Sbostic }
64146366Sbostic
64246366Sbostic /* Not found */
64350997Sbostic switch (action) {
64450997Sbostic case HASH_PUT:
64550997Sbostic case HASH_PUTNEW:
64657586Sbostic if (__addel(hashp, rbufp, key, val)) {
64750997Sbostic save_bufp->flags &= ~BUF_PIN;
64850997Sbostic return (ERROR);
64946366Sbostic } else {
65050997Sbostic save_bufp->flags &= ~BUF_PIN;
65150997Sbostic return (SUCCESS);
65246366Sbostic }
65350997Sbostic case HASH_GET:
65450997Sbostic case HASH_DELETE:
65550997Sbostic default:
65646457Sbostic save_bufp->flags &= ~BUF_PIN;
65750997Sbostic return (ABNORMAL);
65846366Sbostic }
65946366Sbostic
66046366Sbostic found:
66146366Sbostic switch (action) {
66250997Sbostic case HASH_PUTNEW:
66346457Sbostic save_bufp->flags &= ~BUF_PIN;
66450997Sbostic return (ABNORMAL);
66550997Sbostic case HASH_GET:
66646366Sbostic bp = (u_short *)rbufp->page;
66751072Sbostic if (bp[ndx + 1] < REAL_KEY) {
66857586Sbostic if (__big_return(hashp, rbufp, ndx, val, 0))
66951057Sbostic return (ERROR);
67051072Sbostic } else {
67150997Sbostic val->data = (u_char *)rbufp->page + (int)bp[ndx + 1];
67250997Sbostic val->size = bp[ndx] - bp[ndx + 1];
67346366Sbostic }
67446366Sbostic break;
67550997Sbostic case HASH_PUT:
67657586Sbostic if ((__delpair(hashp, rbufp, ndx)) ||
67757586Sbostic (__addel(hashp, rbufp, key, val))) {
67850997Sbostic save_bufp->flags &= ~BUF_PIN;
67950997Sbostic return (ERROR);
68046457Sbostic }
68146366Sbostic break;
68250997Sbostic case HASH_DELETE:
68357586Sbostic if (__delpair(hashp, rbufp, ndx))
68450997Sbostic return (ERROR);
68546366Sbostic break;
68657586Sbostic default:
68757586Sbostic abort();
68846366Sbostic }
68946457Sbostic save_bufp->flags &= ~BUF_PIN;
69046366Sbostic return (SUCCESS);
69146366Sbostic }
69246366Sbostic
69346366Sbostic static int
hash_seq(dbp,key,data,flag)69446366Sbostic hash_seq(dbp, key, data, flag)
69550997Sbostic const DB *dbp;
69650997Sbostic DBT *key, *data;
69750997Sbostic u_int flag;
69846366Sbostic {
69950997Sbostic register u_int bucket;
70050997Sbostic register BUFHEAD *bufp;
70157586Sbostic HTAB *hashp;
70250997Sbostic u_short *bp, ndx;
70346366Sbostic
70457586Sbostic hashp = (HTAB *)dbp->internal;
70550997Sbostic if (flag && flag != R_FIRST && flag != R_NEXT) {
70650997Sbostic hashp->errno = errno = EINVAL;
70750997Sbostic return (ERROR);
70846366Sbostic }
70946366Sbostic #ifdef HASH_STATISTICS
71046366Sbostic hash_accesses++;
71146366Sbostic #endif
71250997Sbostic if ((hashp->cbucket < 0) || (flag == R_FIRST)) {
71350997Sbostic hashp->cbucket = 0;
71450997Sbostic hashp->cndx = 1;
71550997Sbostic hashp->cpage = NULL;
71646366Sbostic }
71746366Sbostic
71855874Sbostic for (bp = NULL; !bp || !bp[0]; ) {
71955452Sbostic if (!(bufp = hashp->cpage)) {
72055452Sbostic for (bucket = hashp->cbucket;
72155452Sbostic bucket <= hashp->MAX_BUCKET;
72255452Sbostic bucket++, hashp->cndx = 1) {
72357586Sbostic bufp = __get_buf(hashp, bucket, NULL, 0);
72455452Sbostic if (!bufp)
72555452Sbostic return (ERROR);
72655452Sbostic hashp->cpage = bufp;
72755452Sbostic bp = (u_short *)bufp->page;
72855452Sbostic if (bp[0])
72955452Sbostic break;
73055452Sbostic }
73155452Sbostic hashp->cbucket = bucket;
73255452Sbostic if (hashp->cbucket > hashp->MAX_BUCKET) {
73355452Sbostic hashp->cbucket = -1;
73455452Sbostic return (ABNORMAL);
73555452Sbostic }
73655452Sbostic } else
73755452Sbostic bp = (u_short *)hashp->cpage->page;
73855452Sbostic
73955452Sbostic #ifdef DEBUG
74055452Sbostic assert(bp);
74155452Sbostic assert(bufp);
74255452Sbostic #endif
74355452Sbostic while (bp[hashp->cndx + 1] == OVFLPAGE) {
74455452Sbostic bufp = hashp->cpage =
74557586Sbostic __get_buf(hashp, bp[hashp->cndx], bufp, 0);
74650997Sbostic if (!bufp)
74750997Sbostic return (ERROR);
74855452Sbostic bp = (u_short *)(bufp->page);
74955452Sbostic hashp->cndx = 1;
75050997Sbostic }
75155874Sbostic if (!bp[0]) {
75255452Sbostic hashp->cpage = NULL;
75355874Sbostic ++hashp->cbucket;
75455874Sbostic }
75546366Sbostic }
75646366Sbostic ndx = hashp->cndx;
75750997Sbostic if (bp[ndx + 1] < REAL_KEY) {
75857586Sbostic if (__big_keydata(hashp, bufp, key, data, 1))
75950997Sbostic return (ERROR);
76046366Sbostic } else {
76150997Sbostic key->data = (u_char *)hashp->cpage->page + bp[ndx];
76250997Sbostic key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx];
76350997Sbostic data->data = (u_char *)hashp->cpage->page + bp[ndx + 1];
76450997Sbostic data->size = bp[ndx] - bp[ndx + 1];
76550997Sbostic ndx += 2;
76650997Sbostic if (ndx > bp[0]) {
76750997Sbostic hashp->cpage = NULL;
76850997Sbostic hashp->cbucket++;
76950997Sbostic hashp->cndx = 1;
77050997Sbostic } else
77150997Sbostic hashp->cndx = ndx;
77246366Sbostic }
77346366Sbostic return (SUCCESS);
77446366Sbostic }
77546366Sbostic
77646366Sbostic /********************************* UTILITIES ************************/
77750997Sbostic
77846366Sbostic /*
77950997Sbostic * Returns:
78050997Sbostic * 0 ==> OK
78150997Sbostic * -1 ==> Error
78250997Sbostic */
78346366Sbostic extern int
78457586Sbostic __expand_table(hashp)
78557586Sbostic HTAB *hashp;
78646366Sbostic {
78750997Sbostic u_int old_bucket, new_bucket;
78850997Sbostic int dirsize, new_segnum, spare_ndx;
78950997Sbostic
79046366Sbostic #ifdef HASH_STATISTICS
79146366Sbostic hash_expansions++;
79246366Sbostic #endif
79346366Sbostic new_bucket = ++hashp->MAX_BUCKET;
79446366Sbostic old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK);
79546366Sbostic
79646366Sbostic new_segnum = new_bucket >> hashp->SSHIFT;
79746366Sbostic
79846366Sbostic /* Check if we need a new segment */
79950997Sbostic if (new_segnum >= hashp->nsegs) {
80050997Sbostic /* Check if we need to expand directory */
80150997Sbostic if (new_segnum >= hashp->DSIZE) {
80250997Sbostic /* Reallocate directory */
80350997Sbostic dirsize = hashp->DSIZE * sizeof(SEGMENT *);
80450997Sbostic if (!hash_realloc(&hashp->dir, dirsize, dirsize << 1))
80550997Sbostic return (-1);
80650997Sbostic hashp->DSIZE = dirsize << 1;
80746366Sbostic }
808*66215Sbostic if ((hashp->dir[new_segnum] =
809*66215Sbostic (SEGMENT)calloc(hashp->SGSIZE, sizeof(SEGMENT))) == NULL)
81050997Sbostic return (-1);
81150997Sbostic hashp->exsegs++;
81250997Sbostic hashp->nsegs++;
81346366Sbostic }
81446366Sbostic /*
81550997Sbostic * If the split point is increasing (MAX_BUCKET's log base 2
81650997Sbostic * * increases), we need to copy the current contents of the spare
81750997Sbostic * split bucket to the next bucket.
81850997Sbostic */
81952001Sbostic spare_ndx = __log2(hashp->MAX_BUCKET + 1);
82052001Sbostic if (spare_ndx > hashp->OVFL_POINT) {
82151061Sbostic hashp->SPARES[spare_ndx] = hashp->SPARES[hashp->OVFL_POINT];
82251061Sbostic hashp->OVFL_POINT = spare_ndx;
82351061Sbostic }
82451061Sbostic
82550997Sbostic if (new_bucket > hashp->HIGH_MASK) {
82650997Sbostic /* Starting a new doubling */
82750997Sbostic hashp->LOW_MASK = hashp->HIGH_MASK;
82850997Sbostic hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK;
82946366Sbostic }
83050997Sbostic /* Relocate records to the new bucket */
83157586Sbostic return (__split_page(hashp, old_bucket, new_bucket));
83250997Sbostic }
83346366Sbostic
83446366Sbostic /*
83550997Sbostic * If realloc guarantees that the pointer is not destroyed if the realloc
83650997Sbostic * fails, then this routine can go away.
83750997Sbostic */
83850997Sbostic static void *
hash_realloc(p_ptr,oldsize,newsize)83950997Sbostic hash_realloc(p_ptr, oldsize, newsize)
84050997Sbostic SEGMENT **p_ptr;
84150997Sbostic int oldsize, newsize;
84246366Sbostic {
84350997Sbostic register void *p;
84446366Sbostic
84550997Sbostic if (p = malloc(newsize)) {
84658016Sbostic memmove(p, *p_ptr, oldsize);
84763747Sbostic memset((char *)p + oldsize, 0, newsize - oldsize);
84846366Sbostic free(*p_ptr);
84946366Sbostic *p_ptr = p;
85046366Sbostic }
85146366Sbostic return (p);
85246366Sbostic }
85346366Sbostic
85447251Sbostic extern u_int
85557586Sbostic __call_hash(hashp, k, len)
85657586Sbostic HTAB *hashp;
85750997Sbostic char *k;
85850997Sbostic int len;
85946366Sbostic {
86050997Sbostic int n, bucket;
86150997Sbostic
86246366Sbostic n = hashp->hash(k, len);
86346366Sbostic bucket = n & hashp->HIGH_MASK;
86450997Sbostic if (bucket > hashp->MAX_BUCKET)
86550997Sbostic bucket = bucket & hashp->LOW_MASK;
86650997Sbostic return (bucket);
86746366Sbostic }
86846366Sbostic
86946366Sbostic /*
87050997Sbostic * Allocate segment table. On error, destroy the table and set errno.
87150997Sbostic *
87250997Sbostic * Returns 0 on success
87350997Sbostic */
87446366Sbostic static int
alloc_segs(hashp,nsegs)87557586Sbostic alloc_segs(hashp, nsegs)
87657586Sbostic HTAB *hashp;
87750997Sbostic int nsegs;
87846366Sbostic {
87950997Sbostic register int i;
88050997Sbostic register SEGMENT store;
88146366Sbostic
88250997Sbostic int save_errno;
88346366Sbostic
884*66215Sbostic if ((hashp->dir =
885*66215Sbostic (SEGMENT *)calloc(hashp->DSIZE, sizeof(SEGMENT *))) == NULL) {
88650997Sbostic save_errno = errno;
88757586Sbostic (void)hdestroy(hashp);
88850997Sbostic errno = save_errno;
88950997Sbostic return (-1);
89050997Sbostic }
89150997Sbostic /* Allocate segments */
892*66215Sbostic if ((store =
893*66215Sbostic (SEGMENT)calloc(nsegs << hashp->SSHIFT, sizeof(SEGMENT))) == NULL) {
89450997Sbostic save_errno = errno;
89557586Sbostic (void)hdestroy(hashp);
89650997Sbostic errno = save_errno;
89750997Sbostic return (-1);
89850997Sbostic }
89950997Sbostic for (i = 0; i < nsegs; i++, hashp->nsegs++)
90050997Sbostic hashp->dir[i] = &store[i << hashp->SSHIFT];
90150997Sbostic return (0);
90246366Sbostic }
90346366Sbostic
90450997Sbostic #if BYTE_ORDER == LITTLE_ENDIAN
90546366Sbostic /*
90650997Sbostic * Hashp->hdr needs to be byteswapped.
90750997Sbostic */
90846366Sbostic static void
swap_header_copy(srcp,destp)90950997Sbostic swap_header_copy(srcp, destp)
91050997Sbostic HASHHDR *srcp, *destp;
91146366Sbostic {
91250997Sbostic int i;
91346366Sbostic
91466193Sbostic P_32_COPY(srcp->magic, destp->magic);
91566193Sbostic P_32_COPY(srcp->version, destp->version);
91666193Sbostic P_32_COPY(srcp->lorder, destp->lorder);
91766193Sbostic P_32_COPY(srcp->bsize, destp->bsize);
91866193Sbostic P_32_COPY(srcp->bshift, destp->bshift);
91966193Sbostic P_32_COPY(srcp->dsize, destp->dsize);
92066193Sbostic P_32_COPY(srcp->ssize, destp->ssize);
92166193Sbostic P_32_COPY(srcp->sshift, destp->sshift);
92266193Sbostic P_32_COPY(srcp->ovfl_point, destp->ovfl_point);
92366193Sbostic P_32_COPY(srcp->last_freed, destp->last_freed);
92466193Sbostic P_32_COPY(srcp->max_bucket, destp->max_bucket);
92566193Sbostic P_32_COPY(srcp->high_mask, destp->high_mask);
92666193Sbostic P_32_COPY(srcp->low_mask, destp->low_mask);
92766193Sbostic P_32_COPY(srcp->ffactor, destp->ffactor);
92866193Sbostic P_32_COPY(srcp->nkeys, destp->nkeys);
92966193Sbostic P_32_COPY(srcp->hdrpages, destp->hdrpages);
93066193Sbostic P_32_COPY(srcp->h_charkey, destp->h_charkey);
93150997Sbostic for (i = 0; i < NCACHED; i++) {
93266193Sbostic P_32_COPY(srcp->spares[i], destp->spares[i]);
93366193Sbostic P_16_COPY(srcp->bitmaps[i], destp->bitmaps[i]);
93450997Sbostic }
93546366Sbostic }
93646366Sbostic
93746366Sbostic static void
swap_header(hashp)93857586Sbostic swap_header(hashp)
93957586Sbostic HTAB *hashp;
94046366Sbostic {
94150997Sbostic HASHHDR *hdrp;
94250997Sbostic int i;
94346366Sbostic
94450997Sbostic hdrp = &hashp->hdr;
94546366Sbostic
94666193Sbostic M_32_SWAP(hdrp->magic);
94766193Sbostic M_32_SWAP(hdrp->version);
94866193Sbostic M_32_SWAP(hdrp->lorder);
94966193Sbostic M_32_SWAP(hdrp->bsize);
95066193Sbostic M_32_SWAP(hdrp->bshift);
95166193Sbostic M_32_SWAP(hdrp->dsize);
95266193Sbostic M_32_SWAP(hdrp->ssize);
95366193Sbostic M_32_SWAP(hdrp->sshift);
95466193Sbostic M_32_SWAP(hdrp->ovfl_point);
95566193Sbostic M_32_SWAP(hdrp->last_freed);
95666193Sbostic M_32_SWAP(hdrp->max_bucket);
95766193Sbostic M_32_SWAP(hdrp->high_mask);
95866193Sbostic M_32_SWAP(hdrp->low_mask);
95966193Sbostic M_32_SWAP(hdrp->ffactor);
96066193Sbostic M_32_SWAP(hdrp->nkeys);
96166193Sbostic M_32_SWAP(hdrp->hdrpages);
96266193Sbostic M_32_SWAP(hdrp->h_charkey);
96350997Sbostic for (i = 0; i < NCACHED; i++) {
96466193Sbostic M_32_SWAP(hdrp->spares[i]);
96566193Sbostic M_16_SWAP(hdrp->bitmaps[i]);
96650997Sbostic }
96746366Sbostic }
96850997Sbostic #endif
969