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*66193Sbostic static char sccsid[] = "@(#)hash.c 8.6 (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 8550997Sbostic if (!(hashp = 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; 16850997Sbostic if (!(dbp = 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 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 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 * 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 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 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 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 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 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 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 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 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 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 } 80850997Sbostic if (!(hashp->dir[new_segnum] = 80950997Sbostic calloc(hashp->SGSIZE, sizeof(SEGMENT)))) 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 * 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 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 88450997Sbostic if (!(hashp->dir = calloc(hashp->DSIZE, sizeof(SEGMENT *)))) { 88550997Sbostic save_errno = errno; 88657586Sbostic (void)hdestroy(hashp); 88750997Sbostic errno = save_errno; 88850997Sbostic return (-1); 88950997Sbostic } 89050997Sbostic /* Allocate segments */ 89150997Sbostic store = calloc(nsegs << hashp->SSHIFT, sizeof(SEGMENT)); 89250997Sbostic if (!store) { 89350997Sbostic save_errno = errno; 89457586Sbostic (void)hdestroy(hashp); 89550997Sbostic errno = save_errno; 89650997Sbostic return (-1); 89750997Sbostic } 89850997Sbostic for (i = 0; i < nsegs; i++, hashp->nsegs++) 89950997Sbostic hashp->dir[i] = &store[i << hashp->SSHIFT]; 90050997Sbostic return (0); 90146366Sbostic } 90246366Sbostic 90350997Sbostic #if BYTE_ORDER == LITTLE_ENDIAN 90446366Sbostic /* 90550997Sbostic * Hashp->hdr needs to be byteswapped. 90650997Sbostic */ 90746366Sbostic static void 90850997Sbostic swap_header_copy(srcp, destp) 90950997Sbostic HASHHDR *srcp, *destp; 91046366Sbostic { 91150997Sbostic int i; 91246366Sbostic 913*66193Sbostic P_32_COPY(srcp->magic, destp->magic); 914*66193Sbostic P_32_COPY(srcp->version, destp->version); 915*66193Sbostic P_32_COPY(srcp->lorder, destp->lorder); 916*66193Sbostic P_32_COPY(srcp->bsize, destp->bsize); 917*66193Sbostic P_32_COPY(srcp->bshift, destp->bshift); 918*66193Sbostic P_32_COPY(srcp->dsize, destp->dsize); 919*66193Sbostic P_32_COPY(srcp->ssize, destp->ssize); 920*66193Sbostic P_32_COPY(srcp->sshift, destp->sshift); 921*66193Sbostic P_32_COPY(srcp->ovfl_point, destp->ovfl_point); 922*66193Sbostic P_32_COPY(srcp->last_freed, destp->last_freed); 923*66193Sbostic P_32_COPY(srcp->max_bucket, destp->max_bucket); 924*66193Sbostic P_32_COPY(srcp->high_mask, destp->high_mask); 925*66193Sbostic P_32_COPY(srcp->low_mask, destp->low_mask); 926*66193Sbostic P_32_COPY(srcp->ffactor, destp->ffactor); 927*66193Sbostic P_32_COPY(srcp->nkeys, destp->nkeys); 928*66193Sbostic P_32_COPY(srcp->hdrpages, destp->hdrpages); 929*66193Sbostic P_32_COPY(srcp->h_charkey, destp->h_charkey); 93050997Sbostic for (i = 0; i < NCACHED; i++) { 931*66193Sbostic P_32_COPY(srcp->spares[i], destp->spares[i]); 932*66193Sbostic P_16_COPY(srcp->bitmaps[i], destp->bitmaps[i]); 93350997Sbostic } 93446366Sbostic } 93546366Sbostic 93646366Sbostic static void 93757586Sbostic swap_header(hashp) 93857586Sbostic HTAB *hashp; 93946366Sbostic { 94050997Sbostic HASHHDR *hdrp; 94150997Sbostic int i; 94246366Sbostic 94350997Sbostic hdrp = &hashp->hdr; 94446366Sbostic 945*66193Sbostic M_32_SWAP(hdrp->magic); 946*66193Sbostic M_32_SWAP(hdrp->version); 947*66193Sbostic M_32_SWAP(hdrp->lorder); 948*66193Sbostic M_32_SWAP(hdrp->bsize); 949*66193Sbostic M_32_SWAP(hdrp->bshift); 950*66193Sbostic M_32_SWAP(hdrp->dsize); 951*66193Sbostic M_32_SWAP(hdrp->ssize); 952*66193Sbostic M_32_SWAP(hdrp->sshift); 953*66193Sbostic M_32_SWAP(hdrp->ovfl_point); 954*66193Sbostic M_32_SWAP(hdrp->last_freed); 955*66193Sbostic M_32_SWAP(hdrp->max_bucket); 956*66193Sbostic M_32_SWAP(hdrp->high_mask); 957*66193Sbostic M_32_SWAP(hdrp->low_mask); 958*66193Sbostic M_32_SWAP(hdrp->ffactor); 959*66193Sbostic M_32_SWAP(hdrp->nkeys); 960*66193Sbostic M_32_SWAP(hdrp->hdrpages); 961*66193Sbostic M_32_SWAP(hdrp->h_charkey); 96250997Sbostic for (i = 0; i < NCACHED; i++) { 963*66193Sbostic M_32_SWAP(hdrp->spares[i]); 964*66193Sbostic M_16_SWAP(hdrp->bitmaps[i]); 96550997Sbostic } 96646366Sbostic } 96750997Sbostic #endif 968