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*64708Sbostic static char sccsid[] = "@(#)hash.c 8.4 (Berkeley) 10/12/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)); 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); 130*64708Sbostic #define OLDHASHVERSION 1 131*64708Sbostic if (hashp->VERSION != HASHVERSION && 132*64708Sbostic 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 41050997Sbostic if (save_errno) { 41150997Sbostic errno = save_errno; 41250997Sbostic return (ERROR); 41346366Sbostic } 41450997Sbostic return (SUCCESS); 41546366Sbostic } 41650997Sbostic /* 41750997Sbostic * Write modified pages to disk 41850997Sbostic * 41950997Sbostic * Returns: 42050997Sbostic * 0 == OK 42150997Sbostic * -1 ERROR 42250997Sbostic */ 42346366Sbostic static int 42460060Sbostic hash_sync(dbp, flags) 42550997Sbostic const DB *dbp; 42660060Sbostic u_int flags; 42746366Sbostic { 42857586Sbostic HTAB *hashp; 42957586Sbostic 43060060Sbostic if (flags != 0) { 43160060Sbostic errno = EINVAL; 43260060Sbostic return (ERROR); 43360060Sbostic } 43460060Sbostic 43550997Sbostic if (!dbp) 43650997Sbostic return (ERROR); 43757586Sbostic 43846366Sbostic hashp = (HTAB *)dbp->internal; 43950997Sbostic if (!hashp->save_file) 44050997Sbostic return (0); 44157586Sbostic if (__buf_free(hashp, 0, 1) || flush_meta(hashp)) 44250997Sbostic return (ERROR); 44346366Sbostic hashp->new_file = 0; 44450997Sbostic return (0); 44546366Sbostic } 44646366Sbostic 44746366Sbostic /* 44850997Sbostic * Returns: 44950997Sbostic * 0 == OK 45050997Sbostic * -1 indicates that errno should be set 45150997Sbostic */ 45246366Sbostic static int 45357586Sbostic flush_meta(hashp) 45457586Sbostic HTAB *hashp; 45546366Sbostic { 45651814Smarc HASHHDR *whdrp; 45751814Smarc #if BYTE_ORDER == LITTLE_ENDIAN 45851814Smarc HASHHDR whdr; 45951814Smarc #endif 46050997Sbostic int fp, i, wsize; 46146366Sbostic 46250997Sbostic if (!hashp->save_file) 46350997Sbostic return (0); 46446366Sbostic hashp->MAGIC = HASHMAGIC; 46551061Sbostic hashp->VERSION = HASHVERSION; 46650997Sbostic hashp->H_CHARKEY = hashp->hash(CHARKEY, sizeof(CHARKEY)); 46746366Sbostic 46846366Sbostic fp = hashp->fp; 46946366Sbostic whdrp = &hashp->hdr; 47046366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN 47146366Sbostic whdrp = &whdr; 47250997Sbostic swap_header_copy(&hashp->hdr, whdrp); 47346366Sbostic #endif 47456669Sbostic if ((lseek(fp, (off_t)0, SEEK_SET) == -1) || 47550997Sbostic ((wsize = write(fp, whdrp, sizeof(HASHHDR))) == -1)) 47650997Sbostic return (-1); 47750997Sbostic else 47850997Sbostic if (wsize != sizeof(HASHHDR)) { 47950997Sbostic errno = EFTYPE; 48050997Sbostic hashp->errno = errno; 48150997Sbostic return (-1); 48246366Sbostic } 48350997Sbostic for (i = 0; i < NCACHED; i++) 48450997Sbostic if (hashp->mapp[i]) 48557586Sbostic if (__put_page(hashp, (char *)hashp->mapp[i], 48650997Sbostic hashp->BITMAPS[i], 0, 1)) 48750997Sbostic return (-1); 48850997Sbostic return (0); 48946366Sbostic } 49050997Sbostic 49146366Sbostic /*******************************SEARCH ROUTINES *****************************/ 49246366Sbostic /* 49350997Sbostic * All the access routines return 49450997Sbostic * 49550997Sbostic * Returns: 49650997Sbostic * 0 on SUCCESS 49750997Sbostic * 1 to indicate an external ERROR (i.e. key not found, etc) 49850997Sbostic * -1 to indicate an internal ERROR (i.e. out of memory, etc) 49950997Sbostic */ 50046366Sbostic static int 50150997Sbostic hash_get(dbp, key, data, flag) 50250997Sbostic const DB *dbp; 50351057Sbostic const DBT *key; 50451057Sbostic DBT *data; 50550997Sbostic u_int flag; 50646366Sbostic { 50757586Sbostic HTAB *hashp; 50857586Sbostic 50957586Sbostic hashp = (HTAB *)dbp->internal; 51050997Sbostic if (flag) { 51150997Sbostic hashp->errno = errno = EINVAL; 51250997Sbostic return (ERROR); 51350997Sbostic } 51457586Sbostic return (hash_access(hashp, HASH_GET, (DBT *)key, data)); 51546366Sbostic } 51646366Sbostic 51746366Sbostic static int 51850997Sbostic hash_put(dbp, key, data, flag) 51950997Sbostic const DB *dbp; 52056893Sbostic DBT *key; 52156893Sbostic const DBT *data; 52250997Sbostic u_int flag; 52346366Sbostic { 52457586Sbostic HTAB *hashp; 52557586Sbostic 52657586Sbostic hashp = (HTAB *)dbp->internal; 52750997Sbostic if (flag && flag != R_NOOVERWRITE) { 52850997Sbostic hashp->errno = errno = EINVAL; 52950997Sbostic return (ERROR); 53050997Sbostic } 53150997Sbostic if ((hashp->flags & O_ACCMODE) == O_RDONLY) { 53250997Sbostic hashp->errno = errno = EPERM; 53350997Sbostic return (ERROR); 53450997Sbostic } 53557586Sbostic return (hash_access(hashp, flag == R_NOOVERWRITE ? 53650997Sbostic HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data)); 53746366Sbostic } 53846366Sbostic 53946366Sbostic static int 54050997Sbostic hash_delete(dbp, key, flag) 54150997Sbostic const DB *dbp; 54250997Sbostic const DBT *key; 54350997Sbostic u_int flag; /* Ignored */ 54446366Sbostic { 54557586Sbostic HTAB *hashp; 54657586Sbostic 54757586Sbostic hashp = (HTAB *)dbp->internal; 54850997Sbostic if (flag && flag != R_CURSOR) { 54950997Sbostic hashp->errno = errno = EINVAL; 55050997Sbostic return (ERROR); 55150997Sbostic } 55250997Sbostic if ((hashp->flags & O_ACCMODE) == O_RDONLY) { 55350997Sbostic hashp->errno = errno = EPERM; 55450997Sbostic return (ERROR); 55550997Sbostic } 55657586Sbostic return (hash_access(hashp, HASH_DELETE, (DBT *)key, NULL)); 55746366Sbostic } 55846366Sbostic 55946366Sbostic /* 56050997Sbostic * Assume that hashp has been set in wrapper routine. 56150997Sbostic */ 56246366Sbostic static int 56357586Sbostic hash_access(hashp, action, key, val) 56457586Sbostic HTAB *hashp; 56550997Sbostic ACTION action; 56650997Sbostic DBT *key, *val; 56746366Sbostic { 56850997Sbostic register BUFHEAD *rbufp; 56950997Sbostic BUFHEAD *bufp, *save_bufp; 57050997Sbostic register u_short *bp; 57150997Sbostic register int n, ndx, off, size; 57250997Sbostic register char *kp; 57350997Sbostic u_short pageno; 57446366Sbostic 57546366Sbostic #ifdef HASH_STATISTICS 57646366Sbostic hash_accesses++; 57746366Sbostic #endif 57846366Sbostic 57950997Sbostic off = hashp->BSIZE; 58046366Sbostic size = key->size; 58146950Sbostic kp = (char *)key->data; 58257586Sbostic rbufp = __get_buf(hashp, __call_hash(hashp, kp, size), NULL, 0); 58350997Sbostic if (!rbufp) 58450997Sbostic return (ERROR); 58546457Sbostic save_bufp = rbufp; 58646366Sbostic 58746457Sbostic /* Pin the bucket chain */ 58846457Sbostic rbufp->flags |= BUF_PIN; 58950997Sbostic for (bp = (u_short *)rbufp->page, n = *bp++, ndx = 1; ndx < n;) 59050997Sbostic if (bp[1] >= REAL_KEY) { 59150997Sbostic /* Real key/data pair */ 59250997Sbostic if (size == off - *bp && 59358016Sbostic memcmp(kp, rbufp->page + *bp, size) == 0) 59450997Sbostic goto found; 59550997Sbostic off = bp[1]; 59646366Sbostic #ifdef HASH_STATISTICS 59750997Sbostic hash_collisions++; 59846366Sbostic #endif 59950997Sbostic bp += 2; 60050997Sbostic ndx += 2; 60150997Sbostic } else if (bp[1] == OVFLPAGE) { 60257586Sbostic rbufp = __get_buf(hashp, *bp, rbufp, 0); 60350997Sbostic if (!rbufp) { 60450997Sbostic save_bufp->flags &= ~BUF_PIN; 60550997Sbostic return (ERROR); 60650997Sbostic } 60750997Sbostic /* FOR LOOP INIT */ 60850997Sbostic bp = (u_short *)rbufp->page; 60950997Sbostic n = *bp++; 61050997Sbostic ndx = 1; 61150997Sbostic off = hashp->BSIZE; 61250997Sbostic } else if (bp[1] < REAL_KEY) { 61357586Sbostic if ((ndx = 61457586Sbostic __find_bigpair(hashp, rbufp, ndx, kp, size)) > 0) 61550997Sbostic goto found; 61650997Sbostic if (ndx == -2) { 61750997Sbostic bufp = rbufp; 61857586Sbostic if (!(pageno = 61957586Sbostic __find_last_page(hashp, &bufp))) { 62050997Sbostic ndx = 0; 62150997Sbostic rbufp = bufp; 62250997Sbostic break; /* FOR */ 62350997Sbostic } 62457586Sbostic rbufp = __get_buf(hashp, pageno, bufp, 0); 62550997Sbostic if (!rbufp) { 62650997Sbostic save_bufp->flags &= ~BUF_PIN; 62750997Sbostic return (ERROR); 62850997Sbostic } 62950997Sbostic /* FOR LOOP INIT */ 63050997Sbostic bp = (u_short *)rbufp->page; 63150997Sbostic n = *bp++; 63250997Sbostic ndx = 1; 63350997Sbostic off = hashp->BSIZE; 63450997Sbostic } else { 63550997Sbostic save_bufp->flags &= ~BUF_PIN; 63650997Sbostic return (ERROR); 63750997Sbostic } 63846457Sbostic } 63946366Sbostic 64046366Sbostic /* Not found */ 64150997Sbostic switch (action) { 64250997Sbostic case HASH_PUT: 64350997Sbostic case HASH_PUTNEW: 64457586Sbostic if (__addel(hashp, rbufp, key, val)) { 64550997Sbostic save_bufp->flags &= ~BUF_PIN; 64650997Sbostic return (ERROR); 64746366Sbostic } else { 64850997Sbostic save_bufp->flags &= ~BUF_PIN; 64950997Sbostic return (SUCCESS); 65046366Sbostic } 65150997Sbostic case HASH_GET: 65250997Sbostic case HASH_DELETE: 65350997Sbostic default: 65446457Sbostic save_bufp->flags &= ~BUF_PIN; 65550997Sbostic return (ABNORMAL); 65646366Sbostic } 65746366Sbostic 65846366Sbostic found: 65946366Sbostic switch (action) { 66050997Sbostic case HASH_PUTNEW: 66146457Sbostic save_bufp->flags &= ~BUF_PIN; 66250997Sbostic return (ABNORMAL); 66350997Sbostic case HASH_GET: 66446366Sbostic bp = (u_short *)rbufp->page; 66551072Sbostic if (bp[ndx + 1] < REAL_KEY) { 66657586Sbostic if (__big_return(hashp, rbufp, ndx, val, 0)) 66751057Sbostic return (ERROR); 66851072Sbostic } else { 66950997Sbostic val->data = (u_char *)rbufp->page + (int)bp[ndx + 1]; 67050997Sbostic val->size = bp[ndx] - bp[ndx + 1]; 67146366Sbostic } 67246366Sbostic break; 67350997Sbostic case HASH_PUT: 67457586Sbostic if ((__delpair(hashp, rbufp, ndx)) || 67557586Sbostic (__addel(hashp, rbufp, key, val))) { 67650997Sbostic save_bufp->flags &= ~BUF_PIN; 67750997Sbostic return (ERROR); 67846457Sbostic } 67946366Sbostic break; 68050997Sbostic case HASH_DELETE: 68157586Sbostic if (__delpair(hashp, rbufp, ndx)) 68250997Sbostic return (ERROR); 68346366Sbostic break; 68457586Sbostic default: 68557586Sbostic abort(); 68646366Sbostic } 68746457Sbostic save_bufp->flags &= ~BUF_PIN; 68846366Sbostic return (SUCCESS); 68946366Sbostic } 69046366Sbostic 69146366Sbostic static int 69246366Sbostic hash_seq(dbp, key, data, flag) 69350997Sbostic const DB *dbp; 69450997Sbostic DBT *key, *data; 69550997Sbostic u_int flag; 69646366Sbostic { 69750997Sbostic register u_int bucket; 69850997Sbostic register BUFHEAD *bufp; 69957586Sbostic HTAB *hashp; 70050997Sbostic u_short *bp, ndx; 70146366Sbostic 70257586Sbostic hashp = (HTAB *)dbp->internal; 70350997Sbostic if (flag && flag != R_FIRST && flag != R_NEXT) { 70450997Sbostic hashp->errno = errno = EINVAL; 70550997Sbostic return (ERROR); 70646366Sbostic } 70746366Sbostic #ifdef HASH_STATISTICS 70846366Sbostic hash_accesses++; 70946366Sbostic #endif 71050997Sbostic if ((hashp->cbucket < 0) || (flag == R_FIRST)) { 71150997Sbostic hashp->cbucket = 0; 71250997Sbostic hashp->cndx = 1; 71350997Sbostic hashp->cpage = NULL; 71446366Sbostic } 71546366Sbostic 71655874Sbostic for (bp = NULL; !bp || !bp[0]; ) { 71755452Sbostic if (!(bufp = hashp->cpage)) { 71855452Sbostic for (bucket = hashp->cbucket; 71955452Sbostic bucket <= hashp->MAX_BUCKET; 72055452Sbostic bucket++, hashp->cndx = 1) { 72157586Sbostic bufp = __get_buf(hashp, bucket, NULL, 0); 72255452Sbostic if (!bufp) 72355452Sbostic return (ERROR); 72455452Sbostic hashp->cpage = bufp; 72555452Sbostic bp = (u_short *)bufp->page; 72655452Sbostic if (bp[0]) 72755452Sbostic break; 72855452Sbostic } 72955452Sbostic hashp->cbucket = bucket; 73055452Sbostic if (hashp->cbucket > hashp->MAX_BUCKET) { 73155452Sbostic hashp->cbucket = -1; 73255452Sbostic return (ABNORMAL); 73355452Sbostic } 73455452Sbostic } else 73555452Sbostic bp = (u_short *)hashp->cpage->page; 73655452Sbostic 73755452Sbostic #ifdef DEBUG 73855452Sbostic assert(bp); 73955452Sbostic assert(bufp); 74055452Sbostic #endif 74155452Sbostic while (bp[hashp->cndx + 1] == OVFLPAGE) { 74255452Sbostic bufp = hashp->cpage = 74357586Sbostic __get_buf(hashp, bp[hashp->cndx], bufp, 0); 74450997Sbostic if (!bufp) 74550997Sbostic return (ERROR); 74655452Sbostic bp = (u_short *)(bufp->page); 74755452Sbostic hashp->cndx = 1; 74850997Sbostic } 74955874Sbostic if (!bp[0]) { 75055452Sbostic hashp->cpage = NULL; 75155874Sbostic ++hashp->cbucket; 75255874Sbostic } 75346366Sbostic } 75446366Sbostic ndx = hashp->cndx; 75550997Sbostic if (bp[ndx + 1] < REAL_KEY) { 75657586Sbostic if (__big_keydata(hashp, bufp, key, data, 1)) 75750997Sbostic return (ERROR); 75846366Sbostic } else { 75950997Sbostic key->data = (u_char *)hashp->cpage->page + bp[ndx]; 76050997Sbostic key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx]; 76150997Sbostic data->data = (u_char *)hashp->cpage->page + bp[ndx + 1]; 76250997Sbostic data->size = bp[ndx] - bp[ndx + 1]; 76350997Sbostic ndx += 2; 76450997Sbostic if (ndx > bp[0]) { 76550997Sbostic hashp->cpage = NULL; 76650997Sbostic hashp->cbucket++; 76750997Sbostic hashp->cndx = 1; 76850997Sbostic } else 76950997Sbostic hashp->cndx = ndx; 77046366Sbostic } 77146366Sbostic return (SUCCESS); 77246366Sbostic } 77346366Sbostic 77446366Sbostic /********************************* UTILITIES ************************/ 77550997Sbostic 77646366Sbostic /* 77750997Sbostic * Returns: 77850997Sbostic * 0 ==> OK 77950997Sbostic * -1 ==> Error 78050997Sbostic */ 78146366Sbostic extern int 78257586Sbostic __expand_table(hashp) 78357586Sbostic HTAB *hashp; 78446366Sbostic { 78550997Sbostic u_int old_bucket, new_bucket; 78650997Sbostic int dirsize, new_segnum, spare_ndx; 78750997Sbostic 78846366Sbostic #ifdef HASH_STATISTICS 78946366Sbostic hash_expansions++; 79046366Sbostic #endif 79146366Sbostic new_bucket = ++hashp->MAX_BUCKET; 79246366Sbostic old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK); 79346366Sbostic 79446366Sbostic new_segnum = new_bucket >> hashp->SSHIFT; 79546366Sbostic 79646366Sbostic /* Check if we need a new segment */ 79750997Sbostic if (new_segnum >= hashp->nsegs) { 79850997Sbostic /* Check if we need to expand directory */ 79950997Sbostic if (new_segnum >= hashp->DSIZE) { 80050997Sbostic /* Reallocate directory */ 80150997Sbostic dirsize = hashp->DSIZE * sizeof(SEGMENT *); 80250997Sbostic if (!hash_realloc(&hashp->dir, dirsize, dirsize << 1)) 80350997Sbostic return (-1); 80450997Sbostic hashp->DSIZE = dirsize << 1; 80546366Sbostic } 80650997Sbostic if (!(hashp->dir[new_segnum] = 80750997Sbostic calloc(hashp->SGSIZE, sizeof(SEGMENT)))) 80850997Sbostic return (-1); 80950997Sbostic hashp->exsegs++; 81050997Sbostic hashp->nsegs++; 81146366Sbostic } 81246366Sbostic /* 81350997Sbostic * If the split point is increasing (MAX_BUCKET's log base 2 81450997Sbostic * * increases), we need to copy the current contents of the spare 81550997Sbostic * split bucket to the next bucket. 81650997Sbostic */ 81752001Sbostic spare_ndx = __log2(hashp->MAX_BUCKET + 1); 81852001Sbostic if (spare_ndx > hashp->OVFL_POINT) { 81951061Sbostic hashp->SPARES[spare_ndx] = hashp->SPARES[hashp->OVFL_POINT]; 82051061Sbostic hashp->OVFL_POINT = spare_ndx; 82151061Sbostic } 82251061Sbostic 82350997Sbostic if (new_bucket > hashp->HIGH_MASK) { 82450997Sbostic /* Starting a new doubling */ 82550997Sbostic hashp->LOW_MASK = hashp->HIGH_MASK; 82650997Sbostic hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK; 82746366Sbostic } 82850997Sbostic /* Relocate records to the new bucket */ 82957586Sbostic return (__split_page(hashp, old_bucket, new_bucket)); 83050997Sbostic } 83146366Sbostic 83246366Sbostic /* 83350997Sbostic * If realloc guarantees that the pointer is not destroyed if the realloc 83450997Sbostic * fails, then this routine can go away. 83550997Sbostic */ 83650997Sbostic static void * 83750997Sbostic hash_realloc(p_ptr, oldsize, newsize) 83850997Sbostic SEGMENT **p_ptr; 83950997Sbostic int oldsize, newsize; 84046366Sbostic { 84150997Sbostic register void *p; 84246366Sbostic 84350997Sbostic if (p = malloc(newsize)) { 84458016Sbostic memmove(p, *p_ptr, oldsize); 84563747Sbostic memset((char *)p + oldsize, 0, newsize - oldsize); 84646366Sbostic free(*p_ptr); 84746366Sbostic *p_ptr = p; 84846366Sbostic } 84946366Sbostic return (p); 85046366Sbostic } 85146366Sbostic 85247251Sbostic extern u_int 85357586Sbostic __call_hash(hashp, k, len) 85457586Sbostic HTAB *hashp; 85550997Sbostic char *k; 85650997Sbostic int len; 85746366Sbostic { 85850997Sbostic int n, bucket; 85950997Sbostic 86046366Sbostic n = hashp->hash(k, len); 86146366Sbostic bucket = n & hashp->HIGH_MASK; 86250997Sbostic if (bucket > hashp->MAX_BUCKET) 86350997Sbostic bucket = bucket & hashp->LOW_MASK; 86450997Sbostic return (bucket); 86546366Sbostic } 86646366Sbostic 86746366Sbostic /* 86850997Sbostic * Allocate segment table. On error, destroy the table and set errno. 86950997Sbostic * 87050997Sbostic * Returns 0 on success 87150997Sbostic */ 87246366Sbostic static int 87357586Sbostic alloc_segs(hashp, nsegs) 87457586Sbostic HTAB *hashp; 87550997Sbostic int nsegs; 87646366Sbostic { 87750997Sbostic register int i; 87850997Sbostic register SEGMENT store; 87946366Sbostic 88050997Sbostic int save_errno; 88146366Sbostic 88250997Sbostic if (!(hashp->dir = calloc(hashp->DSIZE, sizeof(SEGMENT *)))) { 88350997Sbostic save_errno = errno; 88457586Sbostic (void)hdestroy(hashp); 88550997Sbostic errno = save_errno; 88650997Sbostic return (-1); 88750997Sbostic } 88850997Sbostic /* Allocate segments */ 88950997Sbostic store = calloc(nsegs << hashp->SSHIFT, sizeof(SEGMENT)); 89050997Sbostic if (!store) { 89150997Sbostic save_errno = errno; 89257586Sbostic (void)hdestroy(hashp); 89350997Sbostic errno = save_errno; 89450997Sbostic return (-1); 89550997Sbostic } 89650997Sbostic for (i = 0; i < nsegs; i++, hashp->nsegs++) 89750997Sbostic hashp->dir[i] = &store[i << hashp->SSHIFT]; 89850997Sbostic return (0); 89946366Sbostic } 90046366Sbostic 90150997Sbostic #if BYTE_ORDER == LITTLE_ENDIAN 90246366Sbostic /* 90350997Sbostic * Hashp->hdr needs to be byteswapped. 90450997Sbostic */ 90546366Sbostic static void 90650997Sbostic swap_header_copy(srcp, destp) 90750997Sbostic HASHHDR *srcp, *destp; 90846366Sbostic { 90950997Sbostic int i; 91046366Sbostic 91150997Sbostic BLSWAP_COPY(srcp->magic, destp->magic); 91250997Sbostic BLSWAP_COPY(srcp->version, destp->version); 91350997Sbostic BLSWAP_COPY(srcp->lorder, destp->lorder); 91450997Sbostic BLSWAP_COPY(srcp->bsize, destp->bsize); 91550997Sbostic BLSWAP_COPY(srcp->bshift, destp->bshift); 91650997Sbostic BLSWAP_COPY(srcp->dsize, destp->dsize); 91750997Sbostic BLSWAP_COPY(srcp->ssize, destp->ssize); 91850997Sbostic BLSWAP_COPY(srcp->sshift, destp->sshift); 91951061Sbostic BLSWAP_COPY(srcp->ovfl_point, destp->ovfl_point); 92051061Sbostic BLSWAP_COPY(srcp->last_freed, destp->last_freed); 92150997Sbostic BLSWAP_COPY(srcp->max_bucket, destp->max_bucket); 92250997Sbostic BLSWAP_COPY(srcp->high_mask, destp->high_mask); 92350997Sbostic BLSWAP_COPY(srcp->low_mask, destp->low_mask); 92450997Sbostic BLSWAP_COPY(srcp->ffactor, destp->ffactor); 92550997Sbostic BLSWAP_COPY(srcp->nkeys, destp->nkeys); 92650997Sbostic BLSWAP_COPY(srcp->hdrpages, destp->hdrpages); 92750997Sbostic BLSWAP_COPY(srcp->h_charkey, destp->h_charkey); 92850997Sbostic for (i = 0; i < NCACHED; i++) { 92950997Sbostic BLSWAP_COPY(srcp->spares[i], destp->spares[i]); 93050997Sbostic BSSWAP_COPY(srcp->bitmaps[i], destp->bitmaps[i]); 93150997Sbostic } 93246366Sbostic } 93346366Sbostic 93446366Sbostic static void 93557586Sbostic swap_header(hashp) 93657586Sbostic HTAB *hashp; 93746366Sbostic { 93850997Sbostic HASHHDR *hdrp; 93950997Sbostic int i; 94046366Sbostic 94150997Sbostic hdrp = &hashp->hdr; 94246366Sbostic 94350997Sbostic BLSWAP(hdrp->magic); 94450997Sbostic BLSWAP(hdrp->version); 94550997Sbostic BLSWAP(hdrp->lorder); 94650997Sbostic BLSWAP(hdrp->bsize); 94750997Sbostic BLSWAP(hdrp->bshift); 94850997Sbostic BLSWAP(hdrp->dsize); 94950997Sbostic BLSWAP(hdrp->ssize); 95050997Sbostic BLSWAP(hdrp->sshift); 95151061Sbostic BLSWAP(hdrp->ovfl_point); 95251061Sbostic BLSWAP(hdrp->last_freed); 95350997Sbostic BLSWAP(hdrp->max_bucket); 95450997Sbostic BLSWAP(hdrp->high_mask); 95550997Sbostic BLSWAP(hdrp->low_mask); 95650997Sbostic BLSWAP(hdrp->ffactor); 95750997Sbostic BLSWAP(hdrp->nkeys); 95850997Sbostic BLSWAP(hdrp->hdrpages); 95950997Sbostic BLSWAP(hdrp->h_charkey); 96050997Sbostic for (i = 0; i < NCACHED; i++) { 96150997Sbostic BLSWAP(hdrp->spares[i]); 96250997Sbostic BSSWAP(hdrp->bitmaps[i]); 96350997Sbostic } 96446366Sbostic } 96550997Sbostic #endif 966