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*58016Sbostic static char sccsid[] = "@(#)hash.c 5.32 (Berkeley) 02/16/93"; 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)); 3851057Sbostic static int hash_get __P((const DB *, const DBT *, DBT *, u_int)); 3956893Sbostic static int hash_put __P((const DB *, DBT *, const DBT *, u_int)); 4050997Sbostic static void *hash_realloc __P((SEGMENT **, int, int)); 4150997Sbostic static int hash_seq __P((const DB *, DBT *, DBT *, u_int)); 4250997Sbostic static int hash_sync __P((const DB *)); 4357586Sbostic static int hdestroy __P((HTAB *)); 4457586Sbostic static HTAB *init_hash __P((HTAB *, HASHINFO *)); 4557586Sbostic static int init_htab __P((HTAB *, int)); 4650997Sbostic #if BYTE_ORDER == LITTLE_ENDIAN 4757586Sbostic static void swap_header __P((HTAB *, void)); 4850997Sbostic static void swap_header_copy __P((HASHHDR *, HASHHDR *)); 4950997Sbostic #endif 5050997Sbostic 5146366Sbostic /* Fast arithmetic, relying on powers of 2, */ 5250997Sbostic #define MOD(x, y) ((x) & ((y) - 1)) 5346366Sbostic 5450997Sbostic #define RETURN_ERROR(ERR, LOC) { save_errno = ERR; goto LOC; } 5546366Sbostic 5646366Sbostic /* Return values */ 5750997Sbostic #define SUCCESS (0) 5850997Sbostic #define ERROR (-1) 5950997Sbostic #define ABNORMAL (1) 6046366Sbostic 6146366Sbostic #ifdef HASH_STATISTICS 6246366Sbostic long hash_accesses, hash_collisions, hash_expansions, hash_overflows; 6346366Sbostic #endif 6446366Sbostic 6550997Sbostic /************************** INTERFACE ROUTINES ***************************/ 6646366Sbostic /* OPEN/CLOSE */ 6746366Sbostic 6850997Sbostic extern DB * 6951072Sbostic __hash_open(file, flags, mode, info) 7050997Sbostic const char *file; 7150997Sbostic int flags, mode; 7250997Sbostic const HASHINFO *info; /* Special directives for create */ 7346366Sbostic { 7457586Sbostic HTAB *hashp; 7550997Sbostic struct stat statbuf; 7650997Sbostic DB *dbp; 7750997Sbostic int bpages, hdrsize, new_table, nsegs, save_errno; 7846366Sbostic 7956661Sbostic if ((flags & O_ACCMODE) == O_WRONLY) { 8056422Smargo errno = EINVAL; 8156422Smargo return (NULL); 8256422Smargo } 8356422Smargo 8450997Sbostic if (!(hashp = calloc(1, sizeof(HTAB)))) 8550997Sbostic return (NULL); 8650997Sbostic hashp->fp = -1; 8750997Sbostic /* 8850997Sbostic * Select flags relevant to us. Even if user wants write only, we need 8950997Sbostic * to be able to read the actual file, so we need to open it read/write. 9050997Sbostic * But, the field in the hashp structure needs to be accurate so that 9150997Sbostic * we can check accesses. 9250997Sbostic */ 9356893Sbostic hashp->flags = flags = flags & __USE_OPEN_FLAGS; 9446366Sbostic 9550997Sbostic new_table = 0; 9650997Sbostic if (!file || (flags & O_TRUNC) || 9750997Sbostic (stat(file, &statbuf) && (errno == ENOENT))) { 9850997Sbostic if (errno == ENOENT) 9950997Sbostic errno = 0; /* Just in case someone looks at errno */ 10050997Sbostic new_table = 1; 10146485Sbostic } 10250997Sbostic if (file) { 10350997Sbostic if ((hashp->fp = open(file, flags, mode)) == -1) 10450997Sbostic RETURN_ERROR(errno, error0); 10550997Sbostic (void)fcntl(hashp->fp, F_SETFD, 1); 10646366Sbostic } 10750997Sbostic if (new_table) { 10857586Sbostic if (!(hashp = init_hash(hashp, (HASHINFO *)info))) 10950997Sbostic RETURN_ERROR(errno, error1); 11050997Sbostic } else { 11150997Sbostic /* Table already exists */ 11250997Sbostic if (info && info->hash) 11350997Sbostic hashp->hash = info->hash; 11450997Sbostic else 11557586Sbostic hashp->hash = __default_hash; 11646366Sbostic 11750997Sbostic hdrsize = read(hashp->fp, &hashp->hdr, sizeof(HASHHDR)); 11846366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN 11957586Sbostic swap_header(hashp); 12046366Sbostic #endif 12150997Sbostic if (hdrsize == -1) 12250997Sbostic RETURN_ERROR(errno, error1); 12350997Sbostic if (hdrsize != sizeof(HASHHDR)) 12450997Sbostic RETURN_ERROR(EFTYPE, error1); 12550997Sbostic /* Verify file type, versions and hash function */ 12650997Sbostic if (hashp->MAGIC != HASHMAGIC) 12750997Sbostic RETURN_ERROR(EFTYPE, error1); 12851061Sbostic if (hashp->VERSION != HASHVERSION) 12950997Sbostic RETURN_ERROR(EFTYPE, error1); 13050997Sbostic if (hashp->hash(CHARKEY, sizeof(CHARKEY)) != hashp->H_CHARKEY) 13150997Sbostic RETURN_ERROR(EFTYPE, error1); 13251057Sbostic /* 13351057Sbostic * Figure out how many segments we need. Max_Bucket is the 13451057Sbostic * maximum bucket number, so the number of buckets is 13551057Sbostic * max_bucket + 1. 13651057Sbostic */ 13751057Sbostic nsegs = (hashp->MAX_BUCKET + 1 + hashp->SGSIZE - 1) / 13850997Sbostic hashp->SGSIZE; 13950997Sbostic hashp->nsegs = 0; 14057586Sbostic if (alloc_segs(hashp, nsegs)) 14150997Sbostic /* 14250997Sbostic * If alloc_segs fails, table will have been destroyed 14350997Sbostic * and errno will have been set. 14450997Sbostic */ 14550997Sbostic return (NULL); 14650997Sbostic /* Read in bitmaps */ 14751061Sbostic bpages = (hashp->SPARES[hashp->OVFL_POINT] + 14850997Sbostic (hashp->BSIZE << BYTE_SHIFT) - 1) >> 14950997Sbostic (hashp->BSHIFT + BYTE_SHIFT); 15046366Sbostic 15150997Sbostic hashp->nmaps = bpages; 15251057Sbostic (void)memset(&hashp->mapp[0], 0, bpages * sizeof(u_long *)); 15346366Sbostic } 15446366Sbostic 15550997Sbostic /* Initialize Buffer Manager */ 15650997Sbostic if (info && info->cachesize) 15757586Sbostic __buf_init(hashp, info->cachesize); 15850997Sbostic else 15957586Sbostic __buf_init(hashp, DEF_BUFSIZE); 16050997Sbostic 16150997Sbostic hashp->new_file = new_table; 16256422Smargo hashp->save_file = file && (hashp->flags & O_RDWR); 16350997Sbostic hashp->cbucket = -1; 16450997Sbostic if (!(dbp = malloc(sizeof(DB)))) { 16550997Sbostic save_errno = errno; 16657586Sbostic hdestroy(hashp); 16750997Sbostic errno = save_errno; 16850997Sbostic return (NULL); 16946366Sbostic } 17057586Sbostic dbp->internal = hashp; 17150997Sbostic dbp->close = hash_close; 17250997Sbostic dbp->del = hash_delete; 17350997Sbostic dbp->get = hash_get; 17450997Sbostic dbp->put = hash_put; 17550997Sbostic dbp->seq = hash_seq; 17650997Sbostic dbp->sync = hash_sync; 17750997Sbostic dbp->type = DB_HASH; 17846366Sbostic 17946366Sbostic #ifdef DEBUG 18050997Sbostic (void)fprintf(stderr, 18151061Sbostic "%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", 18250997Sbostic "init_htab:", 18350997Sbostic "TABLE POINTER ", hashp, 18450997Sbostic "BUCKET SIZE ", hashp->BSIZE, 18550997Sbostic "BUCKET SHIFT ", hashp->BSHIFT, 18650997Sbostic "DIRECTORY SIZE ", hashp->DSIZE, 18750997Sbostic "SEGMENT SIZE ", hashp->SGSIZE, 18850997Sbostic "SEGMENT SHIFT ", hashp->SSHIFT, 18950997Sbostic "FILL FACTOR ", hashp->FFACTOR, 19050997Sbostic "MAX BUCKET ", hashp->MAX_BUCKET, 19151061Sbostic "OVFL POINT ", hashp->OVFL_POINT, 19251061Sbostic "LAST FREED ", hashp->LAST_FREED, 19350997Sbostic "HIGH MASK ", hashp->HIGH_MASK, 19450997Sbostic "LOW MASK ", hashp->LOW_MASK, 19550997Sbostic "NSEGS ", hashp->nsegs, 19650997Sbostic "NKEYS ", hashp->NKEYS); 19746366Sbostic #endif 19846366Sbostic #ifdef HASH_STATISTICS 19946366Sbostic hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0; 20046366Sbostic #endif 20150997Sbostic return (dbp); 20246366Sbostic 20346366Sbostic error1: 20451192Sbostic if (hashp != NULL) 20551192Sbostic (void)close(hashp->fp); 20646366Sbostic 20746366Sbostic error0: 20850997Sbostic free(hashp); 20950997Sbostic errno = save_errno; 21050997Sbostic return (NULL); 21146366Sbostic } 21246366Sbostic 21346366Sbostic static int 21450997Sbostic hash_close(dbp) 21550997Sbostic DB *dbp; 21646366Sbostic { 21757586Sbostic HTAB *hashp; 21850997Sbostic int retval; 21946457Sbostic 22050997Sbostic if (!dbp) 22150997Sbostic return (ERROR); 22257586Sbostic 22346366Sbostic hashp = (HTAB *)dbp->internal; 22457586Sbostic retval = hdestroy(hashp); 22550997Sbostic free(dbp); 22650997Sbostic return (retval); 22746366Sbostic } 22846366Sbostic 22946366Sbostic /************************** LOCAL CREATION ROUTINES **********************/ 23050997Sbostic static HTAB * 23157586Sbostic init_hash(hashp, info) 23257586Sbostic HTAB *hashp; 23350997Sbostic HASHINFO *info; 23446366Sbostic { 23550997Sbostic int nelem; 23646366Sbostic 23746366Sbostic nelem = 1; 23851061Sbostic hashp->NKEYS = 0; 23950997Sbostic hashp->LORDER = BYTE_ORDER; 24050997Sbostic hashp->BSIZE = DEF_BUCKET_SIZE; 24150997Sbostic hashp->BSHIFT = DEF_BUCKET_SHIFT; 24250997Sbostic hashp->SGSIZE = DEF_SEGSIZE; 24350997Sbostic hashp->SSHIFT = DEF_SEGSIZE_SHIFT; 24450997Sbostic hashp->DSIZE = DEF_DIRSIZE; 24550997Sbostic hashp->FFACTOR = DEF_FFACTOR; 24657586Sbostic hashp->hash = __default_hash; 247*58016Sbostic memset(hashp->SPARES, 0, sizeof(hashp->SPARES)); 248*58016Sbostic memset(hashp->BITMAPS, 0, sizeof (hashp->BITMAPS)); 24946366Sbostic 25050997Sbostic if (info) { 25150997Sbostic if (info->bsize) { 25250997Sbostic /* Round pagesize up to power of 2 */ 25350997Sbostic hashp->BSHIFT = __log2(info->bsize); 25450997Sbostic hashp->BSIZE = 1 << hashp->BSHIFT; 25550997Sbostic if (hashp->BSIZE > MAX_BSIZE) { 25650997Sbostic errno = EINVAL; 25750997Sbostic return (NULL); 25850997Sbostic } 25946366Sbostic } 26050997Sbostic if (info->ffactor) 26150997Sbostic hashp->FFACTOR = info->ffactor; 26250997Sbostic if (info->hash) 26350997Sbostic hashp->hash = info->hash; 26450997Sbostic if (info->nelem) 26550997Sbostic nelem = info->nelem; 26650997Sbostic if (info->lorder) { 26750997Sbostic if (info->lorder != BIG_ENDIAN && 26850997Sbostic info->lorder != LITTLE_ENDIAN) { 26950997Sbostic errno = EINVAL; 27050997Sbostic return (NULL); 27150997Sbostic } 27250997Sbostic hashp->LORDER = info->lorder; 27346366Sbostic } 27446366Sbostic } 27546366Sbostic /* init_htab should destroy the table and set errno if it fails */ 27657586Sbostic if (init_htab(hashp, nelem)) 27750997Sbostic return (NULL); 27850997Sbostic else 27950997Sbostic return (hashp); 28046366Sbostic } 28146366Sbostic /* 28250997Sbostic * This calls alloc_segs which may run out of memory. Alloc_segs will destroy 28350997Sbostic * the table and set errno, so we just pass the error information along. 28450997Sbostic * 28550997Sbostic * Returns 0 on No Error 28650997Sbostic */ 28746366Sbostic static int 28857586Sbostic init_htab(hashp, nelem) 28957586Sbostic HTAB *hashp; 29050997Sbostic int nelem; 29146366Sbostic { 29250997Sbostic register int nbuckets, nsegs; 29350997Sbostic int l2; 29446366Sbostic 29550997Sbostic /* 29650997Sbostic * Divide number of elements by the fill factor and determine a 29750997Sbostic * desired number of buckets. Allocate space for the next greater 29850997Sbostic * power of two number of buckets. 29950997Sbostic */ 30046366Sbostic nelem = (nelem - 1) / hashp->FFACTOR + 1; 30146366Sbostic 30252001Sbostic l2 = __log2(MAX(nelem, 2)); 30346366Sbostic nbuckets = 1 << l2; 30446366Sbostic 30546366Sbostic hashp->SPARES[l2] = l2 + 1; 30650997Sbostic hashp->SPARES[l2 + 1] = l2 + 1; 30751061Sbostic hashp->OVFL_POINT = l2; 30851061Sbostic hashp->LAST_FREED = 2; 30951061Sbostic 31050997Sbostic /* First bitmap page is at: splitpoint l2 page offset 1 */ 31157586Sbostic if (__init_bitmap(hashp, OADDR_OF(l2, 1), l2 + 1, 0)) 31251057Sbostic return (-1); 31346366Sbostic 31446366Sbostic hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1; 31546366Sbostic hashp->HIGH_MASK = (nbuckets << 1) - 1; 31650997Sbostic hashp->HDRPAGES = ((MAX(sizeof(HASHHDR), MINHDRSIZE) - 1) >> 31750997Sbostic hashp->BSHIFT) + 1; 31846366Sbostic 31946366Sbostic nsegs = (nbuckets - 1) / hashp->SGSIZE + 1; 32046366Sbostic nsegs = 1 << __log2(nsegs); 32146366Sbostic 32250997Sbostic if (nsegs > hashp->DSIZE) 32350997Sbostic hashp->DSIZE = nsegs; 32457586Sbostic return (alloc_segs(hashp, nsegs)); 32546366Sbostic } 32646366Sbostic 32746366Sbostic /********************** DESTROY/CLOSE ROUTINES ************************/ 32846366Sbostic 32946366Sbostic /* 33050997Sbostic * Flushes any changes to the file if necessary and destroys the hashp 33150997Sbostic * structure, freeing all allocated space. 33250997Sbostic */ 33346366Sbostic static int 33457586Sbostic hdestroy(hashp) 33557586Sbostic HTAB *hashp; 33646366Sbostic { 33750997Sbostic int i, save_errno; 33846366Sbostic 33946366Sbostic save_errno = 0; 34046366Sbostic 34146366Sbostic #ifdef HASH_STATISTICS 34257586Sbostic (void)fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n", 34357586Sbostic hash_accesses, hash_collisions); 34457586Sbostic (void)fprintf(stderr, "hdestroy: expansions %ld\n", 34557586Sbostic hash_expansions); 34657586Sbostic (void)fprintf(stderr, "hdestroy: overflows %ld\n", 34757586Sbostic hash_overflows); 34857586Sbostic (void)fprintf(stderr, "keys %ld maxp %d segmentcount %d\n", 34957586Sbostic hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs); 35046366Sbostic 35157586Sbostic for (i = 0; i < NCACHED; i++) 35257586Sbostic (void)fprintf(stderr, 35357586Sbostic "spares[%d] = %d\n", i, hashp->SPARES[i]); 35446366Sbostic #endif 35557586Sbostic /* 35657586Sbostic * Call on buffer manager to free buffers, and if required, 35757586Sbostic * write them to disk. 35857586Sbostic */ 35957586Sbostic if (__buf_free(hashp, 1, hashp->save_file)) 36057586Sbostic save_errno = errno; 36157586Sbostic if (hashp->dir) { 36257586Sbostic free(*hashp->dir); /* Free initial segments */ 36357586Sbostic /* Free extra segments */ 36457586Sbostic while (hashp->exsegs--) 36557586Sbostic free(hashp->dir[--hashp->nsegs]); 36657586Sbostic free(hashp->dir); 36757586Sbostic } 36857586Sbostic if (flush_meta(hashp) && !save_errno) 36957586Sbostic save_errno = errno; 37057586Sbostic /* Free Bigmaps */ 37157586Sbostic for (i = 0; i < hashp->nmaps; i++) 37257586Sbostic if (hashp->mapp[i]) 37357586Sbostic free(hashp->mapp[i]); 37446503Sbostic 37557586Sbostic if (hashp->fp != -1) 37657586Sbostic (void)close(hashp->fp); 37757586Sbostic 37850997Sbostic if (save_errno) { 37950997Sbostic errno = save_errno; 38050997Sbostic return (ERROR); 38146366Sbostic } 38250997Sbostic return (SUCCESS); 38346366Sbostic } 38450997Sbostic /* 38550997Sbostic * Write modified pages to disk 38650997Sbostic * 38750997Sbostic * Returns: 38850997Sbostic * 0 == OK 38950997Sbostic * -1 ERROR 39050997Sbostic */ 39146366Sbostic static int 39246366Sbostic hash_sync(dbp) 39350997Sbostic const DB *dbp; 39446366Sbostic { 39557586Sbostic HTAB *hashp; 39657586Sbostic 39750997Sbostic if (!dbp) 39850997Sbostic return (ERROR); 39957586Sbostic 40046366Sbostic hashp = (HTAB *)dbp->internal; 40150997Sbostic if (!hashp->save_file) 40250997Sbostic return (0); 40357586Sbostic if (__buf_free(hashp, 0, 1) || flush_meta(hashp)) 40450997Sbostic return (ERROR); 40546366Sbostic hashp->new_file = 0; 40650997Sbostic return (0); 40746366Sbostic } 40846366Sbostic 40946366Sbostic /* 41050997Sbostic * Returns: 41150997Sbostic * 0 == OK 41250997Sbostic * -1 indicates that errno should be set 41350997Sbostic */ 41446366Sbostic static int 41557586Sbostic flush_meta(hashp) 41657586Sbostic HTAB *hashp; 41746366Sbostic { 41851814Smarc HASHHDR *whdrp; 41951814Smarc #if BYTE_ORDER == LITTLE_ENDIAN 42051814Smarc HASHHDR whdr; 42151814Smarc #endif 42250997Sbostic int fp, i, wsize; 42346366Sbostic 42450997Sbostic if (!hashp->save_file) 42550997Sbostic return (0); 42646366Sbostic hashp->MAGIC = HASHMAGIC; 42751061Sbostic hashp->VERSION = HASHVERSION; 42850997Sbostic hashp->H_CHARKEY = hashp->hash(CHARKEY, sizeof(CHARKEY)); 42946366Sbostic 43046366Sbostic fp = hashp->fp; 43146366Sbostic whdrp = &hashp->hdr; 43246366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN 43346366Sbostic whdrp = &whdr; 43450997Sbostic swap_header_copy(&hashp->hdr, whdrp); 43546366Sbostic #endif 43656669Sbostic if ((lseek(fp, (off_t)0, SEEK_SET) == -1) || 43750997Sbostic ((wsize = write(fp, whdrp, sizeof(HASHHDR))) == -1)) 43850997Sbostic return (-1); 43950997Sbostic else 44050997Sbostic if (wsize != sizeof(HASHHDR)) { 44150997Sbostic errno = EFTYPE; 44250997Sbostic hashp->errno = errno; 44350997Sbostic return (-1); 44446366Sbostic } 44550997Sbostic for (i = 0; i < NCACHED; i++) 44650997Sbostic if (hashp->mapp[i]) 44757586Sbostic if (__put_page(hashp, (char *)hashp->mapp[i], 44850997Sbostic hashp->BITMAPS[i], 0, 1)) 44950997Sbostic return (-1); 45050997Sbostic return (0); 45146366Sbostic } 45250997Sbostic 45346366Sbostic /*******************************SEARCH ROUTINES *****************************/ 45446366Sbostic /* 45550997Sbostic * All the access routines return 45650997Sbostic * 45750997Sbostic * Returns: 45850997Sbostic * 0 on SUCCESS 45950997Sbostic * 1 to indicate an external ERROR (i.e. key not found, etc) 46050997Sbostic * -1 to indicate an internal ERROR (i.e. out of memory, etc) 46150997Sbostic */ 46246366Sbostic static int 46350997Sbostic hash_get(dbp, key, data, flag) 46450997Sbostic const DB *dbp; 46551057Sbostic const DBT *key; 46651057Sbostic DBT *data; 46750997Sbostic u_int flag; 46846366Sbostic { 46957586Sbostic HTAB *hashp; 47057586Sbostic 47157586Sbostic hashp = (HTAB *)dbp->internal; 47250997Sbostic if (flag) { 47350997Sbostic hashp->errno = errno = EINVAL; 47450997Sbostic return (ERROR); 47550997Sbostic } 47657586Sbostic return (hash_access(hashp, HASH_GET, (DBT *)key, data)); 47746366Sbostic } 47846366Sbostic 47946366Sbostic static int 48050997Sbostic hash_put(dbp, key, data, flag) 48150997Sbostic const DB *dbp; 48256893Sbostic DBT *key; 48356893Sbostic const DBT *data; 48450997Sbostic u_int flag; 48546366Sbostic { 48657586Sbostic HTAB *hashp; 48757586Sbostic 48857586Sbostic hashp = (HTAB *)dbp->internal; 48950997Sbostic if (flag && flag != R_NOOVERWRITE) { 49050997Sbostic hashp->errno = errno = EINVAL; 49150997Sbostic return (ERROR); 49250997Sbostic } 49350997Sbostic if ((hashp->flags & O_ACCMODE) == O_RDONLY) { 49450997Sbostic hashp->errno = errno = EPERM; 49550997Sbostic return (ERROR); 49650997Sbostic } 49757586Sbostic return (hash_access(hashp, flag == R_NOOVERWRITE ? 49850997Sbostic HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data)); 49946366Sbostic } 50046366Sbostic 50146366Sbostic static int 50250997Sbostic hash_delete(dbp, key, flag) 50350997Sbostic const DB *dbp; 50450997Sbostic const DBT *key; 50550997Sbostic u_int flag; /* Ignored */ 50646366Sbostic { 50757586Sbostic HTAB *hashp; 50857586Sbostic 50957586Sbostic hashp = (HTAB *)dbp->internal; 51050997Sbostic if (flag && flag != R_CURSOR) { 51150997Sbostic hashp->errno = errno = EINVAL; 51250997Sbostic return (ERROR); 51350997Sbostic } 51450997Sbostic if ((hashp->flags & O_ACCMODE) == O_RDONLY) { 51550997Sbostic hashp->errno = errno = EPERM; 51650997Sbostic return (ERROR); 51750997Sbostic } 51857586Sbostic return (hash_access(hashp, HASH_DELETE, (DBT *)key, NULL)); 51946366Sbostic } 52046366Sbostic 52146366Sbostic /* 52250997Sbostic * Assume that hashp has been set in wrapper routine. 52350997Sbostic */ 52446366Sbostic static int 52557586Sbostic hash_access(hashp, action, key, val) 52657586Sbostic HTAB *hashp; 52750997Sbostic ACTION action; 52850997Sbostic DBT *key, *val; 52946366Sbostic { 53050997Sbostic register BUFHEAD *rbufp; 53150997Sbostic BUFHEAD *bufp, *save_bufp; 53250997Sbostic register u_short *bp; 53350997Sbostic register int n, ndx, off, size; 53450997Sbostic register char *kp; 53550997Sbostic u_short pageno; 53646366Sbostic 53746366Sbostic #ifdef HASH_STATISTICS 53846366Sbostic hash_accesses++; 53946366Sbostic #endif 54046366Sbostic 54150997Sbostic off = hashp->BSIZE; 54246366Sbostic size = key->size; 54346950Sbostic kp = (char *)key->data; 54457586Sbostic rbufp = __get_buf(hashp, __call_hash(hashp, kp, size), NULL, 0); 54550997Sbostic if (!rbufp) 54650997Sbostic return (ERROR); 54746457Sbostic save_bufp = rbufp; 54846366Sbostic 54946457Sbostic /* Pin the bucket chain */ 55046457Sbostic rbufp->flags |= BUF_PIN; 55150997Sbostic for (bp = (u_short *)rbufp->page, n = *bp++, ndx = 1; ndx < n;) 55250997Sbostic if (bp[1] >= REAL_KEY) { 55350997Sbostic /* Real key/data pair */ 55450997Sbostic if (size == off - *bp && 555*58016Sbostic memcmp(kp, rbufp->page + *bp, size) == 0) 55650997Sbostic goto found; 55750997Sbostic off = bp[1]; 55846366Sbostic #ifdef HASH_STATISTICS 55950997Sbostic hash_collisions++; 56046366Sbostic #endif 56150997Sbostic bp += 2; 56250997Sbostic ndx += 2; 56350997Sbostic } else if (bp[1] == OVFLPAGE) { 56457586Sbostic rbufp = __get_buf(hashp, *bp, rbufp, 0); 56550997Sbostic if (!rbufp) { 56650997Sbostic save_bufp->flags &= ~BUF_PIN; 56750997Sbostic return (ERROR); 56850997Sbostic } 56950997Sbostic /* FOR LOOP INIT */ 57050997Sbostic bp = (u_short *)rbufp->page; 57150997Sbostic n = *bp++; 57250997Sbostic ndx = 1; 57350997Sbostic off = hashp->BSIZE; 57450997Sbostic } else if (bp[1] < REAL_KEY) { 57557586Sbostic if ((ndx = 57657586Sbostic __find_bigpair(hashp, rbufp, ndx, kp, size)) > 0) 57750997Sbostic goto found; 57850997Sbostic if (ndx == -2) { 57950997Sbostic bufp = rbufp; 58057586Sbostic if (!(pageno = 58157586Sbostic __find_last_page(hashp, &bufp))) { 58250997Sbostic ndx = 0; 58350997Sbostic rbufp = bufp; 58450997Sbostic break; /* FOR */ 58550997Sbostic } 58657586Sbostic rbufp = __get_buf(hashp, pageno, bufp, 0); 58750997Sbostic if (!rbufp) { 58850997Sbostic save_bufp->flags &= ~BUF_PIN; 58950997Sbostic return (ERROR); 59050997Sbostic } 59150997Sbostic /* FOR LOOP INIT */ 59250997Sbostic bp = (u_short *)rbufp->page; 59350997Sbostic n = *bp++; 59450997Sbostic ndx = 1; 59550997Sbostic off = hashp->BSIZE; 59650997Sbostic } else { 59750997Sbostic save_bufp->flags &= ~BUF_PIN; 59850997Sbostic return (ERROR); 59950997Sbostic } 60046457Sbostic } 60146366Sbostic 60246366Sbostic /* Not found */ 60350997Sbostic switch (action) { 60450997Sbostic case HASH_PUT: 60550997Sbostic case HASH_PUTNEW: 60657586Sbostic if (__addel(hashp, rbufp, key, val)) { 60750997Sbostic save_bufp->flags &= ~BUF_PIN; 60850997Sbostic return (ERROR); 60946366Sbostic } else { 61050997Sbostic save_bufp->flags &= ~BUF_PIN; 61150997Sbostic return (SUCCESS); 61246366Sbostic } 61350997Sbostic case HASH_GET: 61450997Sbostic case HASH_DELETE: 61550997Sbostic default: 61646457Sbostic save_bufp->flags &= ~BUF_PIN; 61750997Sbostic return (ABNORMAL); 61846366Sbostic } 61946366Sbostic 62046366Sbostic found: 62146366Sbostic switch (action) { 62250997Sbostic case HASH_PUTNEW: 62346457Sbostic save_bufp->flags &= ~BUF_PIN; 62450997Sbostic return (ABNORMAL); 62550997Sbostic case HASH_GET: 62646366Sbostic bp = (u_short *)rbufp->page; 62751072Sbostic if (bp[ndx + 1] < REAL_KEY) { 62857586Sbostic if (__big_return(hashp, rbufp, ndx, val, 0)) 62951057Sbostic return (ERROR); 63051072Sbostic } else { 63150997Sbostic val->data = (u_char *)rbufp->page + (int)bp[ndx + 1]; 63250997Sbostic val->size = bp[ndx] - bp[ndx + 1]; 63346366Sbostic } 63446366Sbostic break; 63550997Sbostic case HASH_PUT: 63657586Sbostic if ((__delpair(hashp, rbufp, ndx)) || 63757586Sbostic (__addel(hashp, rbufp, key, val))) { 63850997Sbostic save_bufp->flags &= ~BUF_PIN; 63950997Sbostic return (ERROR); 64046457Sbostic } 64146366Sbostic break; 64250997Sbostic case HASH_DELETE: 64357586Sbostic if (__delpair(hashp, rbufp, ndx)) 64450997Sbostic return (ERROR); 64546366Sbostic break; 64657586Sbostic default: 64757586Sbostic abort(); 64846366Sbostic } 64946457Sbostic save_bufp->flags &= ~BUF_PIN; 65046366Sbostic return (SUCCESS); 65146366Sbostic } 65246366Sbostic 65346366Sbostic static int 65446366Sbostic hash_seq(dbp, key, data, flag) 65550997Sbostic const DB *dbp; 65650997Sbostic DBT *key, *data; 65750997Sbostic u_int flag; 65846366Sbostic { 65950997Sbostic register u_int bucket; 66050997Sbostic register BUFHEAD *bufp; 66157586Sbostic HTAB *hashp; 66250997Sbostic u_short *bp, ndx; 66346366Sbostic 66457586Sbostic hashp = (HTAB *)dbp->internal; 66550997Sbostic if (flag && flag != R_FIRST && flag != R_NEXT) { 66650997Sbostic hashp->errno = errno = EINVAL; 66750997Sbostic return (ERROR); 66846366Sbostic } 66946366Sbostic #ifdef HASH_STATISTICS 67046366Sbostic hash_accesses++; 67146366Sbostic #endif 67250997Sbostic if ((hashp->cbucket < 0) || (flag == R_FIRST)) { 67350997Sbostic hashp->cbucket = 0; 67450997Sbostic hashp->cndx = 1; 67550997Sbostic hashp->cpage = NULL; 67646366Sbostic } 67746366Sbostic 67855874Sbostic for (bp = NULL; !bp || !bp[0]; ) { 67955452Sbostic if (!(bufp = hashp->cpage)) { 68055452Sbostic for (bucket = hashp->cbucket; 68155452Sbostic bucket <= hashp->MAX_BUCKET; 68255452Sbostic bucket++, hashp->cndx = 1) { 68357586Sbostic bufp = __get_buf(hashp, bucket, NULL, 0); 68455452Sbostic if (!bufp) 68555452Sbostic return (ERROR); 68655452Sbostic hashp->cpage = bufp; 68755452Sbostic bp = (u_short *)bufp->page; 68855452Sbostic if (bp[0]) 68955452Sbostic break; 69055452Sbostic } 69155452Sbostic hashp->cbucket = bucket; 69255452Sbostic if (hashp->cbucket > hashp->MAX_BUCKET) { 69355452Sbostic hashp->cbucket = -1; 69455452Sbostic return (ABNORMAL); 69555452Sbostic } 69655452Sbostic } else 69755452Sbostic bp = (u_short *)hashp->cpage->page; 69855452Sbostic 69955452Sbostic #ifdef DEBUG 70055452Sbostic assert(bp); 70155452Sbostic assert(bufp); 70255452Sbostic #endif 70355452Sbostic while (bp[hashp->cndx + 1] == OVFLPAGE) { 70455452Sbostic bufp = hashp->cpage = 70557586Sbostic __get_buf(hashp, bp[hashp->cndx], bufp, 0); 70650997Sbostic if (!bufp) 70750997Sbostic return (ERROR); 70855452Sbostic bp = (u_short *)(bufp->page); 70955452Sbostic hashp->cndx = 1; 71050997Sbostic } 71155874Sbostic if (!bp[0]) { 71255452Sbostic hashp->cpage = NULL; 71355874Sbostic ++hashp->cbucket; 71455874Sbostic } 71546366Sbostic } 71646366Sbostic ndx = hashp->cndx; 71750997Sbostic if (bp[ndx + 1] < REAL_KEY) { 71857586Sbostic if (__big_keydata(hashp, bufp, key, data, 1)) 71950997Sbostic return (ERROR); 72046366Sbostic } else { 72150997Sbostic key->data = (u_char *)hashp->cpage->page + bp[ndx]; 72250997Sbostic key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx]; 72350997Sbostic data->data = (u_char *)hashp->cpage->page + bp[ndx + 1]; 72450997Sbostic data->size = bp[ndx] - bp[ndx + 1]; 72550997Sbostic ndx += 2; 72650997Sbostic if (ndx > bp[0]) { 72750997Sbostic hashp->cpage = NULL; 72850997Sbostic hashp->cbucket++; 72950997Sbostic hashp->cndx = 1; 73050997Sbostic } else 73150997Sbostic hashp->cndx = ndx; 73246366Sbostic } 73346366Sbostic return (SUCCESS); 73446366Sbostic } 73546366Sbostic 73646366Sbostic /********************************* UTILITIES ************************/ 73750997Sbostic 73846366Sbostic /* 73950997Sbostic * Returns: 74050997Sbostic * 0 ==> OK 74150997Sbostic * -1 ==> Error 74250997Sbostic */ 74346366Sbostic extern int 74457586Sbostic __expand_table(hashp) 74557586Sbostic HTAB *hashp; 74646366Sbostic { 74750997Sbostic u_int old_bucket, new_bucket; 74850997Sbostic int dirsize, new_segnum, spare_ndx; 74950997Sbostic 75046366Sbostic #ifdef HASH_STATISTICS 75146366Sbostic hash_expansions++; 75246366Sbostic #endif 75346366Sbostic new_bucket = ++hashp->MAX_BUCKET; 75446366Sbostic old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK); 75546366Sbostic 75646366Sbostic new_segnum = new_bucket >> hashp->SSHIFT; 75746366Sbostic 75846366Sbostic /* Check if we need a new segment */ 75950997Sbostic if (new_segnum >= hashp->nsegs) { 76050997Sbostic /* Check if we need to expand directory */ 76150997Sbostic if (new_segnum >= hashp->DSIZE) { 76250997Sbostic /* Reallocate directory */ 76350997Sbostic dirsize = hashp->DSIZE * sizeof(SEGMENT *); 76450997Sbostic if (!hash_realloc(&hashp->dir, dirsize, dirsize << 1)) 76550997Sbostic return (-1); 76650997Sbostic hashp->DSIZE = dirsize << 1; 76746366Sbostic } 76850997Sbostic if (!(hashp->dir[new_segnum] = 76950997Sbostic calloc(hashp->SGSIZE, sizeof(SEGMENT)))) 77050997Sbostic return (-1); 77150997Sbostic hashp->exsegs++; 77250997Sbostic hashp->nsegs++; 77346366Sbostic } 77446366Sbostic /* 77550997Sbostic * If the split point is increasing (MAX_BUCKET's log base 2 77650997Sbostic * * increases), we need to copy the current contents of the spare 77750997Sbostic * split bucket to the next bucket. 77850997Sbostic */ 77952001Sbostic spare_ndx = __log2(hashp->MAX_BUCKET + 1); 78052001Sbostic if (spare_ndx > hashp->OVFL_POINT) { 78151061Sbostic hashp->SPARES[spare_ndx] = hashp->SPARES[hashp->OVFL_POINT]; 78251061Sbostic hashp->OVFL_POINT = spare_ndx; 78351061Sbostic } 78451061Sbostic 78550997Sbostic if (new_bucket > hashp->HIGH_MASK) { 78650997Sbostic /* Starting a new doubling */ 78750997Sbostic hashp->LOW_MASK = hashp->HIGH_MASK; 78850997Sbostic hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK; 78946366Sbostic } 79050997Sbostic /* Relocate records to the new bucket */ 79157586Sbostic return (__split_page(hashp, old_bucket, new_bucket)); 79250997Sbostic } 79346366Sbostic 79446366Sbostic /* 79550997Sbostic * If realloc guarantees that the pointer is not destroyed if the realloc 79650997Sbostic * fails, then this routine can go away. 79750997Sbostic */ 79850997Sbostic static void * 79950997Sbostic hash_realloc(p_ptr, oldsize, newsize) 80050997Sbostic SEGMENT **p_ptr; 80150997Sbostic int oldsize, newsize; 80246366Sbostic { 80350997Sbostic register void *p; 80446366Sbostic 80550997Sbostic if (p = malloc(newsize)) { 806*58016Sbostic memmove(p, *p_ptr, oldsize); 807*58016Sbostic memset(*p_ptr + oldsize, 0, newsize - oldsize); 80846366Sbostic free(*p_ptr); 80946366Sbostic *p_ptr = p; 81046366Sbostic } 81146366Sbostic return (p); 81246366Sbostic } 81346366Sbostic 81447251Sbostic extern u_int 81557586Sbostic __call_hash(hashp, k, len) 81657586Sbostic HTAB *hashp; 81750997Sbostic char *k; 81850997Sbostic int len; 81946366Sbostic { 82050997Sbostic int n, bucket; 82150997Sbostic 82246366Sbostic n = hashp->hash(k, len); 82346366Sbostic bucket = n & hashp->HIGH_MASK; 82450997Sbostic if (bucket > hashp->MAX_BUCKET) 82550997Sbostic bucket = bucket & hashp->LOW_MASK; 82650997Sbostic return (bucket); 82746366Sbostic } 82846366Sbostic 82946366Sbostic /* 83050997Sbostic * Allocate segment table. On error, destroy the table and set errno. 83150997Sbostic * 83250997Sbostic * Returns 0 on success 83350997Sbostic */ 83446366Sbostic static int 83557586Sbostic alloc_segs(hashp, nsegs) 83657586Sbostic HTAB *hashp; 83750997Sbostic int nsegs; 83846366Sbostic { 83950997Sbostic register int i; 84050997Sbostic register SEGMENT store; 84146366Sbostic 84250997Sbostic int save_errno; 84346366Sbostic 84450997Sbostic if (!(hashp->dir = calloc(hashp->DSIZE, sizeof(SEGMENT *)))) { 84550997Sbostic save_errno = errno; 84657586Sbostic (void)hdestroy(hashp); 84750997Sbostic errno = save_errno; 84850997Sbostic return (-1); 84950997Sbostic } 85050997Sbostic /* Allocate segments */ 85150997Sbostic store = calloc(nsegs << hashp->SSHIFT, sizeof(SEGMENT)); 85250997Sbostic if (!store) { 85350997Sbostic save_errno = errno; 85457586Sbostic (void)hdestroy(hashp); 85550997Sbostic errno = save_errno; 85650997Sbostic return (-1); 85750997Sbostic } 85850997Sbostic for (i = 0; i < nsegs; i++, hashp->nsegs++) 85950997Sbostic hashp->dir[i] = &store[i << hashp->SSHIFT]; 86050997Sbostic return (0); 86146366Sbostic } 86246366Sbostic 86350997Sbostic #if BYTE_ORDER == LITTLE_ENDIAN 86446366Sbostic /* 86550997Sbostic * Hashp->hdr needs to be byteswapped. 86650997Sbostic */ 86746366Sbostic static void 86850997Sbostic swap_header_copy(srcp, destp) 86950997Sbostic HASHHDR *srcp, *destp; 87046366Sbostic { 87150997Sbostic int i; 87246366Sbostic 87350997Sbostic BLSWAP_COPY(srcp->magic, destp->magic); 87450997Sbostic BLSWAP_COPY(srcp->version, destp->version); 87550997Sbostic BLSWAP_COPY(srcp->lorder, destp->lorder); 87650997Sbostic BLSWAP_COPY(srcp->bsize, destp->bsize); 87750997Sbostic BLSWAP_COPY(srcp->bshift, destp->bshift); 87850997Sbostic BLSWAP_COPY(srcp->dsize, destp->dsize); 87950997Sbostic BLSWAP_COPY(srcp->ssize, destp->ssize); 88050997Sbostic BLSWAP_COPY(srcp->sshift, destp->sshift); 88151061Sbostic BLSWAP_COPY(srcp->ovfl_point, destp->ovfl_point); 88251061Sbostic BLSWAP_COPY(srcp->last_freed, destp->last_freed); 88350997Sbostic BLSWAP_COPY(srcp->max_bucket, destp->max_bucket); 88450997Sbostic BLSWAP_COPY(srcp->high_mask, destp->high_mask); 88550997Sbostic BLSWAP_COPY(srcp->low_mask, destp->low_mask); 88650997Sbostic BLSWAP_COPY(srcp->ffactor, destp->ffactor); 88750997Sbostic BLSWAP_COPY(srcp->nkeys, destp->nkeys); 88850997Sbostic BLSWAP_COPY(srcp->hdrpages, destp->hdrpages); 88950997Sbostic BLSWAP_COPY(srcp->h_charkey, destp->h_charkey); 89050997Sbostic for (i = 0; i < NCACHED; i++) { 89150997Sbostic BLSWAP_COPY(srcp->spares[i], destp->spares[i]); 89250997Sbostic BSSWAP_COPY(srcp->bitmaps[i], destp->bitmaps[i]); 89350997Sbostic } 89446366Sbostic } 89546366Sbostic 89646366Sbostic static void 89757586Sbostic swap_header(hashp) 89857586Sbostic HTAB *hashp; 89946366Sbostic { 90050997Sbostic HASHHDR *hdrp; 90150997Sbostic int i; 90246366Sbostic 90350997Sbostic hdrp = &hashp->hdr; 90446366Sbostic 90550997Sbostic BLSWAP(hdrp->magic); 90650997Sbostic BLSWAP(hdrp->version); 90750997Sbostic BLSWAP(hdrp->lorder); 90850997Sbostic BLSWAP(hdrp->bsize); 90950997Sbostic BLSWAP(hdrp->bshift); 91050997Sbostic BLSWAP(hdrp->dsize); 91150997Sbostic BLSWAP(hdrp->ssize); 91250997Sbostic BLSWAP(hdrp->sshift); 91351061Sbostic BLSWAP(hdrp->ovfl_point); 91451061Sbostic BLSWAP(hdrp->last_freed); 91550997Sbostic BLSWAP(hdrp->max_bucket); 91650997Sbostic BLSWAP(hdrp->high_mask); 91750997Sbostic BLSWAP(hdrp->low_mask); 91850997Sbostic BLSWAP(hdrp->ffactor); 91950997Sbostic BLSWAP(hdrp->nkeys); 92050997Sbostic BLSWAP(hdrp->hdrpages); 92150997Sbostic BLSWAP(hdrp->h_charkey); 92250997Sbostic for (i = 0; i < NCACHED; i++) { 92350997Sbostic BLSWAP(hdrp->spares[i]); 92450997Sbostic BSSWAP(hdrp->bitmaps[i]); 92550997Sbostic } 92646366Sbostic } 92750997Sbostic #endif 928