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*64466Sbostic static char sccsid[] = "@(#)hash.c 8.3 (Berkeley) 09/07/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 * 70*64466Sbostic __hash_open(file, flags, mode, info, dflags) 7150997Sbostic const char *file; 72*64466Sbostic 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; 88*64466Sbostic 8950997Sbostic /* 90*64466Sbostic * Even if user wants write only, we need to be able to read 91*64466Sbostic * the actual file, so we need to open it read/write. But, the 92*64466Sbostic * field in the hashp structure needs to be accurate so that 9350997Sbostic * we can check accesses. 9450997Sbostic */ 95*64466Sbostic 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); 13051061Sbostic if (hashp->VERSION != HASHVERSION) 13150997Sbostic RETURN_ERROR(EFTYPE, error1); 13250997Sbostic if (hashp->hash(CHARKEY, sizeof(CHARKEY)) != hashp->H_CHARKEY) 13350997Sbostic RETURN_ERROR(EFTYPE, error1); 13451057Sbostic /* 13551057Sbostic * Figure out how many segments we need. Max_Bucket is the 13651057Sbostic * maximum bucket number, so the number of buckets is 13751057Sbostic * max_bucket + 1. 13851057Sbostic */ 13951057Sbostic nsegs = (hashp->MAX_BUCKET + 1 + hashp->SGSIZE - 1) / 14050997Sbostic hashp->SGSIZE; 14150997Sbostic hashp->nsegs = 0; 14257586Sbostic if (alloc_segs(hashp, nsegs)) 14350997Sbostic /* 14450997Sbostic * If alloc_segs fails, table will have been destroyed 14550997Sbostic * and errno will have been set. 14650997Sbostic */ 14750997Sbostic return (NULL); 14850997Sbostic /* Read in bitmaps */ 14951061Sbostic bpages = (hashp->SPARES[hashp->OVFL_POINT] + 15050997Sbostic (hashp->BSIZE << BYTE_SHIFT) - 1) >> 15150997Sbostic (hashp->BSHIFT + BYTE_SHIFT); 15246366Sbostic 15350997Sbostic hashp->nmaps = bpages; 15451057Sbostic (void)memset(&hashp->mapp[0], 0, bpages * sizeof(u_long *)); 15546366Sbostic } 15646366Sbostic 15750997Sbostic /* Initialize Buffer Manager */ 15850997Sbostic if (info && info->cachesize) 15957586Sbostic __buf_init(hashp, info->cachesize); 16050997Sbostic else 16157586Sbostic __buf_init(hashp, DEF_BUFSIZE); 16250997Sbostic 16350997Sbostic hashp->new_file = new_table; 16456422Smargo hashp->save_file = file && (hashp->flags & O_RDWR); 16550997Sbostic hashp->cbucket = -1; 16650997Sbostic if (!(dbp = malloc(sizeof(DB)))) { 16750997Sbostic save_errno = errno; 16857586Sbostic hdestroy(hashp); 16950997Sbostic errno = save_errno; 17050997Sbostic return (NULL); 17146366Sbostic } 17257586Sbostic dbp->internal = hashp; 17350997Sbostic dbp->close = hash_close; 17450997Sbostic dbp->del = hash_delete; 17560247Sbostic dbp->fd = hash_fd; 17650997Sbostic dbp->get = hash_get; 17750997Sbostic dbp->put = hash_put; 17850997Sbostic dbp->seq = hash_seq; 17950997Sbostic dbp->sync = hash_sync; 18050997Sbostic dbp->type = DB_HASH; 18146366Sbostic 18246366Sbostic #ifdef DEBUG 18350997Sbostic (void)fprintf(stderr, 18451061Sbostic "%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", 18550997Sbostic "init_htab:", 18650997Sbostic "TABLE POINTER ", hashp, 18750997Sbostic "BUCKET SIZE ", hashp->BSIZE, 18850997Sbostic "BUCKET SHIFT ", hashp->BSHIFT, 18950997Sbostic "DIRECTORY SIZE ", hashp->DSIZE, 19050997Sbostic "SEGMENT SIZE ", hashp->SGSIZE, 19150997Sbostic "SEGMENT SHIFT ", hashp->SSHIFT, 19250997Sbostic "FILL FACTOR ", hashp->FFACTOR, 19350997Sbostic "MAX BUCKET ", hashp->MAX_BUCKET, 19451061Sbostic "OVFL POINT ", hashp->OVFL_POINT, 19551061Sbostic "LAST FREED ", hashp->LAST_FREED, 19650997Sbostic "HIGH MASK ", hashp->HIGH_MASK, 19750997Sbostic "LOW MASK ", hashp->LOW_MASK, 19850997Sbostic "NSEGS ", hashp->nsegs, 19950997Sbostic "NKEYS ", hashp->NKEYS); 20046366Sbostic #endif 20146366Sbostic #ifdef HASH_STATISTICS 20246366Sbostic hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0; 20346366Sbostic #endif 20450997Sbostic return (dbp); 20546366Sbostic 20646366Sbostic error1: 20751192Sbostic if (hashp != NULL) 20851192Sbostic (void)close(hashp->fp); 20946366Sbostic 21046366Sbostic error0: 21150997Sbostic free(hashp); 21250997Sbostic errno = save_errno; 21350997Sbostic return (NULL); 21446366Sbostic } 21546366Sbostic 21646366Sbostic static int 21750997Sbostic hash_close(dbp) 21850997Sbostic DB *dbp; 21946366Sbostic { 22057586Sbostic HTAB *hashp; 22150997Sbostic int retval; 22246457Sbostic 22350997Sbostic if (!dbp) 22450997Sbostic return (ERROR); 22557586Sbostic 22646366Sbostic hashp = (HTAB *)dbp->internal; 22757586Sbostic retval = hdestroy(hashp); 22850997Sbostic free(dbp); 22950997Sbostic return (retval); 23046366Sbostic } 23146366Sbostic 23260247Sbostic static int 23360247Sbostic hash_fd(dbp) 23460247Sbostic const DB *dbp; 23560247Sbostic { 23660247Sbostic HTAB *hashp; 23760247Sbostic 23860247Sbostic if (!dbp) 23960247Sbostic return (ERROR); 24060247Sbostic 24160247Sbostic hashp = (HTAB *)dbp->internal; 24260247Sbostic if (hashp->fp == -1) { 24360247Sbostic errno = ENOENT; 24460247Sbostic return (-1); 24560247Sbostic } 24660247Sbostic return (hashp->fp); 24760247Sbostic } 24860247Sbostic 24946366Sbostic /************************** LOCAL CREATION ROUTINES **********************/ 25050997Sbostic static HTAB * 25160247Sbostic init_hash(hashp, file, info) 25257586Sbostic HTAB *hashp; 25360247Sbostic const char *file; 25450997Sbostic HASHINFO *info; 25546366Sbostic { 25660247Sbostic struct stat statbuf; 25750997Sbostic int nelem; 25846366Sbostic 25946366Sbostic nelem = 1; 26051061Sbostic hashp->NKEYS = 0; 26150997Sbostic hashp->LORDER = BYTE_ORDER; 26250997Sbostic hashp->BSIZE = DEF_BUCKET_SIZE; 26350997Sbostic hashp->BSHIFT = DEF_BUCKET_SHIFT; 26450997Sbostic hashp->SGSIZE = DEF_SEGSIZE; 26550997Sbostic hashp->SSHIFT = DEF_SEGSIZE_SHIFT; 26650997Sbostic hashp->DSIZE = DEF_DIRSIZE; 26750997Sbostic hashp->FFACTOR = DEF_FFACTOR; 26857586Sbostic hashp->hash = __default_hash; 26958016Sbostic memset(hashp->SPARES, 0, sizeof(hashp->SPARES)); 27058016Sbostic memset(hashp->BITMAPS, 0, sizeof (hashp->BITMAPS)); 27146366Sbostic 27260247Sbostic /* Fix bucket size to be optimal for file system */ 27360247Sbostic if (file != NULL) { 27460247Sbostic if (stat(file, &statbuf)) 27560247Sbostic return (NULL); 27660247Sbostic hashp->BSIZE = statbuf.st_blksize; 27760247Sbostic hashp->BSHIFT = __log2(hashp->BSIZE); 27860247Sbostic } 27960247Sbostic 28050997Sbostic if (info) { 28150997Sbostic if (info->bsize) { 28250997Sbostic /* Round pagesize up to power of 2 */ 28350997Sbostic hashp->BSHIFT = __log2(info->bsize); 28450997Sbostic hashp->BSIZE = 1 << hashp->BSHIFT; 28550997Sbostic if (hashp->BSIZE > MAX_BSIZE) { 28650997Sbostic errno = EINVAL; 28750997Sbostic return (NULL); 28850997Sbostic } 28946366Sbostic } 29050997Sbostic if (info->ffactor) 29150997Sbostic hashp->FFACTOR = info->ffactor; 29250997Sbostic if (info->hash) 29350997Sbostic hashp->hash = info->hash; 29450997Sbostic if (info->nelem) 29550997Sbostic nelem = info->nelem; 29650997Sbostic if (info->lorder) { 29750997Sbostic if (info->lorder != BIG_ENDIAN && 29850997Sbostic info->lorder != LITTLE_ENDIAN) { 29950997Sbostic errno = EINVAL; 30050997Sbostic return (NULL); 30150997Sbostic } 30250997Sbostic hashp->LORDER = info->lorder; 30346366Sbostic } 30446366Sbostic } 30546366Sbostic /* init_htab should destroy the table and set errno if it fails */ 30657586Sbostic if (init_htab(hashp, nelem)) 30750997Sbostic return (NULL); 30850997Sbostic else 30950997Sbostic return (hashp); 31046366Sbostic } 31146366Sbostic /* 31250997Sbostic * This calls alloc_segs which may run out of memory. Alloc_segs will destroy 31350997Sbostic * the table and set errno, so we just pass the error information along. 31450997Sbostic * 31550997Sbostic * Returns 0 on No Error 31650997Sbostic */ 31746366Sbostic static int 31857586Sbostic init_htab(hashp, nelem) 31957586Sbostic HTAB *hashp; 32050997Sbostic int nelem; 32146366Sbostic { 32250997Sbostic register int nbuckets, nsegs; 32350997Sbostic int l2; 32446366Sbostic 32550997Sbostic /* 32650997Sbostic * Divide number of elements by the fill factor and determine a 32750997Sbostic * desired number of buckets. Allocate space for the next greater 32850997Sbostic * power of two number of buckets. 32950997Sbostic */ 33046366Sbostic nelem = (nelem - 1) / hashp->FFACTOR + 1; 33146366Sbostic 33252001Sbostic l2 = __log2(MAX(nelem, 2)); 33346366Sbostic nbuckets = 1 << l2; 33446366Sbostic 33546366Sbostic hashp->SPARES[l2] = l2 + 1; 33650997Sbostic hashp->SPARES[l2 + 1] = l2 + 1; 33751061Sbostic hashp->OVFL_POINT = l2; 33851061Sbostic hashp->LAST_FREED = 2; 33951061Sbostic 34050997Sbostic /* First bitmap page is at: splitpoint l2 page offset 1 */ 34157586Sbostic if (__init_bitmap(hashp, OADDR_OF(l2, 1), l2 + 1, 0)) 34251057Sbostic return (-1); 34346366Sbostic 34446366Sbostic hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1; 34546366Sbostic hashp->HIGH_MASK = (nbuckets << 1) - 1; 34650997Sbostic hashp->HDRPAGES = ((MAX(sizeof(HASHHDR), MINHDRSIZE) - 1) >> 34750997Sbostic hashp->BSHIFT) + 1; 34846366Sbostic 34946366Sbostic nsegs = (nbuckets - 1) / hashp->SGSIZE + 1; 35046366Sbostic nsegs = 1 << __log2(nsegs); 35146366Sbostic 35250997Sbostic if (nsegs > hashp->DSIZE) 35350997Sbostic hashp->DSIZE = nsegs; 35457586Sbostic return (alloc_segs(hashp, nsegs)); 35546366Sbostic } 35646366Sbostic 35746366Sbostic /********************** DESTROY/CLOSE ROUTINES ************************/ 35846366Sbostic 35946366Sbostic /* 36050997Sbostic * Flushes any changes to the file if necessary and destroys the hashp 36150997Sbostic * structure, freeing all allocated space. 36250997Sbostic */ 36346366Sbostic static int 36457586Sbostic hdestroy(hashp) 36557586Sbostic HTAB *hashp; 36646366Sbostic { 36750997Sbostic int i, save_errno; 36846366Sbostic 36946366Sbostic save_errno = 0; 37046366Sbostic 37146366Sbostic #ifdef HASH_STATISTICS 37257586Sbostic (void)fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n", 37357586Sbostic hash_accesses, hash_collisions); 37457586Sbostic (void)fprintf(stderr, "hdestroy: expansions %ld\n", 37557586Sbostic hash_expansions); 37657586Sbostic (void)fprintf(stderr, "hdestroy: overflows %ld\n", 37757586Sbostic hash_overflows); 37857586Sbostic (void)fprintf(stderr, "keys %ld maxp %d segmentcount %d\n", 37957586Sbostic hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs); 38046366Sbostic 38157586Sbostic for (i = 0; i < NCACHED; i++) 38257586Sbostic (void)fprintf(stderr, 38357586Sbostic "spares[%d] = %d\n", i, hashp->SPARES[i]); 38446366Sbostic #endif 38557586Sbostic /* 38657586Sbostic * Call on buffer manager to free buffers, and if required, 38757586Sbostic * write them to disk. 38857586Sbostic */ 38957586Sbostic if (__buf_free(hashp, 1, hashp->save_file)) 39057586Sbostic save_errno = errno; 39157586Sbostic if (hashp->dir) { 39257586Sbostic free(*hashp->dir); /* Free initial segments */ 39357586Sbostic /* Free extra segments */ 39457586Sbostic while (hashp->exsegs--) 39557586Sbostic free(hashp->dir[--hashp->nsegs]); 39657586Sbostic free(hashp->dir); 39757586Sbostic } 39857586Sbostic if (flush_meta(hashp) && !save_errno) 39957586Sbostic save_errno = errno; 40057586Sbostic /* Free Bigmaps */ 40157586Sbostic for (i = 0; i < hashp->nmaps; i++) 40257586Sbostic if (hashp->mapp[i]) 40357586Sbostic free(hashp->mapp[i]); 40446503Sbostic 40557586Sbostic if (hashp->fp != -1) 40657586Sbostic (void)close(hashp->fp); 40757586Sbostic 40850997Sbostic if (save_errno) { 40950997Sbostic errno = save_errno; 41050997Sbostic return (ERROR); 41146366Sbostic } 41250997Sbostic return (SUCCESS); 41346366Sbostic } 41450997Sbostic /* 41550997Sbostic * Write modified pages to disk 41650997Sbostic * 41750997Sbostic * Returns: 41850997Sbostic * 0 == OK 41950997Sbostic * -1 ERROR 42050997Sbostic */ 42146366Sbostic static int 42260060Sbostic hash_sync(dbp, flags) 42350997Sbostic const DB *dbp; 42460060Sbostic u_int flags; 42546366Sbostic { 42657586Sbostic HTAB *hashp; 42757586Sbostic 42860060Sbostic if (flags != 0) { 42960060Sbostic errno = EINVAL; 43060060Sbostic return (ERROR); 43160060Sbostic } 43260060Sbostic 43350997Sbostic if (!dbp) 43450997Sbostic return (ERROR); 43557586Sbostic 43646366Sbostic hashp = (HTAB *)dbp->internal; 43750997Sbostic if (!hashp->save_file) 43850997Sbostic return (0); 43957586Sbostic if (__buf_free(hashp, 0, 1) || flush_meta(hashp)) 44050997Sbostic return (ERROR); 44146366Sbostic hashp->new_file = 0; 44250997Sbostic return (0); 44346366Sbostic } 44446366Sbostic 44546366Sbostic /* 44650997Sbostic * Returns: 44750997Sbostic * 0 == OK 44850997Sbostic * -1 indicates that errno should be set 44950997Sbostic */ 45046366Sbostic static int 45157586Sbostic flush_meta(hashp) 45257586Sbostic HTAB *hashp; 45346366Sbostic { 45451814Smarc HASHHDR *whdrp; 45551814Smarc #if BYTE_ORDER == LITTLE_ENDIAN 45651814Smarc HASHHDR whdr; 45751814Smarc #endif 45850997Sbostic int fp, i, wsize; 45946366Sbostic 46050997Sbostic if (!hashp->save_file) 46150997Sbostic return (0); 46246366Sbostic hashp->MAGIC = HASHMAGIC; 46351061Sbostic hashp->VERSION = HASHVERSION; 46450997Sbostic hashp->H_CHARKEY = hashp->hash(CHARKEY, sizeof(CHARKEY)); 46546366Sbostic 46646366Sbostic fp = hashp->fp; 46746366Sbostic whdrp = &hashp->hdr; 46846366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN 46946366Sbostic whdrp = &whdr; 47050997Sbostic swap_header_copy(&hashp->hdr, whdrp); 47146366Sbostic #endif 47256669Sbostic if ((lseek(fp, (off_t)0, SEEK_SET) == -1) || 47350997Sbostic ((wsize = write(fp, whdrp, sizeof(HASHHDR))) == -1)) 47450997Sbostic return (-1); 47550997Sbostic else 47650997Sbostic if (wsize != sizeof(HASHHDR)) { 47750997Sbostic errno = EFTYPE; 47850997Sbostic hashp->errno = errno; 47950997Sbostic return (-1); 48046366Sbostic } 48150997Sbostic for (i = 0; i < NCACHED; i++) 48250997Sbostic if (hashp->mapp[i]) 48357586Sbostic if (__put_page(hashp, (char *)hashp->mapp[i], 48450997Sbostic hashp->BITMAPS[i], 0, 1)) 48550997Sbostic return (-1); 48650997Sbostic return (0); 48746366Sbostic } 48850997Sbostic 48946366Sbostic /*******************************SEARCH ROUTINES *****************************/ 49046366Sbostic /* 49150997Sbostic * All the access routines return 49250997Sbostic * 49350997Sbostic * Returns: 49450997Sbostic * 0 on SUCCESS 49550997Sbostic * 1 to indicate an external ERROR (i.e. key not found, etc) 49650997Sbostic * -1 to indicate an internal ERROR (i.e. out of memory, etc) 49750997Sbostic */ 49846366Sbostic static int 49950997Sbostic hash_get(dbp, key, data, flag) 50050997Sbostic const DB *dbp; 50151057Sbostic const DBT *key; 50251057Sbostic DBT *data; 50350997Sbostic u_int flag; 50446366Sbostic { 50557586Sbostic HTAB *hashp; 50657586Sbostic 50757586Sbostic hashp = (HTAB *)dbp->internal; 50850997Sbostic if (flag) { 50950997Sbostic hashp->errno = errno = EINVAL; 51050997Sbostic return (ERROR); 51150997Sbostic } 51257586Sbostic return (hash_access(hashp, HASH_GET, (DBT *)key, data)); 51346366Sbostic } 51446366Sbostic 51546366Sbostic static int 51650997Sbostic hash_put(dbp, key, data, flag) 51750997Sbostic const DB *dbp; 51856893Sbostic DBT *key; 51956893Sbostic const DBT *data; 52050997Sbostic u_int flag; 52146366Sbostic { 52257586Sbostic HTAB *hashp; 52357586Sbostic 52457586Sbostic hashp = (HTAB *)dbp->internal; 52550997Sbostic if (flag && flag != R_NOOVERWRITE) { 52650997Sbostic hashp->errno = errno = EINVAL; 52750997Sbostic return (ERROR); 52850997Sbostic } 52950997Sbostic if ((hashp->flags & O_ACCMODE) == O_RDONLY) { 53050997Sbostic hashp->errno = errno = EPERM; 53150997Sbostic return (ERROR); 53250997Sbostic } 53357586Sbostic return (hash_access(hashp, flag == R_NOOVERWRITE ? 53450997Sbostic HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data)); 53546366Sbostic } 53646366Sbostic 53746366Sbostic static int 53850997Sbostic hash_delete(dbp, key, flag) 53950997Sbostic const DB *dbp; 54050997Sbostic const DBT *key; 54150997Sbostic u_int flag; /* Ignored */ 54246366Sbostic { 54357586Sbostic HTAB *hashp; 54457586Sbostic 54557586Sbostic hashp = (HTAB *)dbp->internal; 54650997Sbostic if (flag && flag != R_CURSOR) { 54750997Sbostic hashp->errno = errno = EINVAL; 54850997Sbostic return (ERROR); 54950997Sbostic } 55050997Sbostic if ((hashp->flags & O_ACCMODE) == O_RDONLY) { 55150997Sbostic hashp->errno = errno = EPERM; 55250997Sbostic return (ERROR); 55350997Sbostic } 55457586Sbostic return (hash_access(hashp, HASH_DELETE, (DBT *)key, NULL)); 55546366Sbostic } 55646366Sbostic 55746366Sbostic /* 55850997Sbostic * Assume that hashp has been set in wrapper routine. 55950997Sbostic */ 56046366Sbostic static int 56157586Sbostic hash_access(hashp, action, key, val) 56257586Sbostic HTAB *hashp; 56350997Sbostic ACTION action; 56450997Sbostic DBT *key, *val; 56546366Sbostic { 56650997Sbostic register BUFHEAD *rbufp; 56750997Sbostic BUFHEAD *bufp, *save_bufp; 56850997Sbostic register u_short *bp; 56950997Sbostic register int n, ndx, off, size; 57050997Sbostic register char *kp; 57150997Sbostic u_short pageno; 57246366Sbostic 57346366Sbostic #ifdef HASH_STATISTICS 57446366Sbostic hash_accesses++; 57546366Sbostic #endif 57646366Sbostic 57750997Sbostic off = hashp->BSIZE; 57846366Sbostic size = key->size; 57946950Sbostic kp = (char *)key->data; 58057586Sbostic rbufp = __get_buf(hashp, __call_hash(hashp, kp, size), NULL, 0); 58150997Sbostic if (!rbufp) 58250997Sbostic return (ERROR); 58346457Sbostic save_bufp = rbufp; 58446366Sbostic 58546457Sbostic /* Pin the bucket chain */ 58646457Sbostic rbufp->flags |= BUF_PIN; 58750997Sbostic for (bp = (u_short *)rbufp->page, n = *bp++, ndx = 1; ndx < n;) 58850997Sbostic if (bp[1] >= REAL_KEY) { 58950997Sbostic /* Real key/data pair */ 59050997Sbostic if (size == off - *bp && 59158016Sbostic memcmp(kp, rbufp->page + *bp, size) == 0) 59250997Sbostic goto found; 59350997Sbostic off = bp[1]; 59446366Sbostic #ifdef HASH_STATISTICS 59550997Sbostic hash_collisions++; 59646366Sbostic #endif 59750997Sbostic bp += 2; 59850997Sbostic ndx += 2; 59950997Sbostic } else if (bp[1] == OVFLPAGE) { 60057586Sbostic rbufp = __get_buf(hashp, *bp, rbufp, 0); 60150997Sbostic if (!rbufp) { 60250997Sbostic save_bufp->flags &= ~BUF_PIN; 60350997Sbostic return (ERROR); 60450997Sbostic } 60550997Sbostic /* FOR LOOP INIT */ 60650997Sbostic bp = (u_short *)rbufp->page; 60750997Sbostic n = *bp++; 60850997Sbostic ndx = 1; 60950997Sbostic off = hashp->BSIZE; 61050997Sbostic } else if (bp[1] < REAL_KEY) { 61157586Sbostic if ((ndx = 61257586Sbostic __find_bigpair(hashp, rbufp, ndx, kp, size)) > 0) 61350997Sbostic goto found; 61450997Sbostic if (ndx == -2) { 61550997Sbostic bufp = rbufp; 61657586Sbostic if (!(pageno = 61757586Sbostic __find_last_page(hashp, &bufp))) { 61850997Sbostic ndx = 0; 61950997Sbostic rbufp = bufp; 62050997Sbostic break; /* FOR */ 62150997Sbostic } 62257586Sbostic rbufp = __get_buf(hashp, pageno, bufp, 0); 62350997Sbostic if (!rbufp) { 62450997Sbostic save_bufp->flags &= ~BUF_PIN; 62550997Sbostic return (ERROR); 62650997Sbostic } 62750997Sbostic /* FOR LOOP INIT */ 62850997Sbostic bp = (u_short *)rbufp->page; 62950997Sbostic n = *bp++; 63050997Sbostic ndx = 1; 63150997Sbostic off = hashp->BSIZE; 63250997Sbostic } else { 63350997Sbostic save_bufp->flags &= ~BUF_PIN; 63450997Sbostic return (ERROR); 63550997Sbostic } 63646457Sbostic } 63746366Sbostic 63846366Sbostic /* Not found */ 63950997Sbostic switch (action) { 64050997Sbostic case HASH_PUT: 64150997Sbostic case HASH_PUTNEW: 64257586Sbostic if (__addel(hashp, rbufp, key, val)) { 64350997Sbostic save_bufp->flags &= ~BUF_PIN; 64450997Sbostic return (ERROR); 64546366Sbostic } else { 64650997Sbostic save_bufp->flags &= ~BUF_PIN; 64750997Sbostic return (SUCCESS); 64846366Sbostic } 64950997Sbostic case HASH_GET: 65050997Sbostic case HASH_DELETE: 65150997Sbostic default: 65246457Sbostic save_bufp->flags &= ~BUF_PIN; 65350997Sbostic return (ABNORMAL); 65446366Sbostic } 65546366Sbostic 65646366Sbostic found: 65746366Sbostic switch (action) { 65850997Sbostic case HASH_PUTNEW: 65946457Sbostic save_bufp->flags &= ~BUF_PIN; 66050997Sbostic return (ABNORMAL); 66150997Sbostic case HASH_GET: 66246366Sbostic bp = (u_short *)rbufp->page; 66351072Sbostic if (bp[ndx + 1] < REAL_KEY) { 66457586Sbostic if (__big_return(hashp, rbufp, ndx, val, 0)) 66551057Sbostic return (ERROR); 66651072Sbostic } else { 66750997Sbostic val->data = (u_char *)rbufp->page + (int)bp[ndx + 1]; 66850997Sbostic val->size = bp[ndx] - bp[ndx + 1]; 66946366Sbostic } 67046366Sbostic break; 67150997Sbostic case HASH_PUT: 67257586Sbostic if ((__delpair(hashp, rbufp, ndx)) || 67357586Sbostic (__addel(hashp, rbufp, key, val))) { 67450997Sbostic save_bufp->flags &= ~BUF_PIN; 67550997Sbostic return (ERROR); 67646457Sbostic } 67746366Sbostic break; 67850997Sbostic case HASH_DELETE: 67957586Sbostic if (__delpair(hashp, rbufp, ndx)) 68050997Sbostic return (ERROR); 68146366Sbostic break; 68257586Sbostic default: 68357586Sbostic abort(); 68446366Sbostic } 68546457Sbostic save_bufp->flags &= ~BUF_PIN; 68646366Sbostic return (SUCCESS); 68746366Sbostic } 68846366Sbostic 68946366Sbostic static int 69046366Sbostic hash_seq(dbp, key, data, flag) 69150997Sbostic const DB *dbp; 69250997Sbostic DBT *key, *data; 69350997Sbostic u_int flag; 69446366Sbostic { 69550997Sbostic register u_int bucket; 69650997Sbostic register BUFHEAD *bufp; 69757586Sbostic HTAB *hashp; 69850997Sbostic u_short *bp, ndx; 69946366Sbostic 70057586Sbostic hashp = (HTAB *)dbp->internal; 70150997Sbostic if (flag && flag != R_FIRST && flag != R_NEXT) { 70250997Sbostic hashp->errno = errno = EINVAL; 70350997Sbostic return (ERROR); 70446366Sbostic } 70546366Sbostic #ifdef HASH_STATISTICS 70646366Sbostic hash_accesses++; 70746366Sbostic #endif 70850997Sbostic if ((hashp->cbucket < 0) || (flag == R_FIRST)) { 70950997Sbostic hashp->cbucket = 0; 71050997Sbostic hashp->cndx = 1; 71150997Sbostic hashp->cpage = NULL; 71246366Sbostic } 71346366Sbostic 71455874Sbostic for (bp = NULL; !bp || !bp[0]; ) { 71555452Sbostic if (!(bufp = hashp->cpage)) { 71655452Sbostic for (bucket = hashp->cbucket; 71755452Sbostic bucket <= hashp->MAX_BUCKET; 71855452Sbostic bucket++, hashp->cndx = 1) { 71957586Sbostic bufp = __get_buf(hashp, bucket, NULL, 0); 72055452Sbostic if (!bufp) 72155452Sbostic return (ERROR); 72255452Sbostic hashp->cpage = bufp; 72355452Sbostic bp = (u_short *)bufp->page; 72455452Sbostic if (bp[0]) 72555452Sbostic break; 72655452Sbostic } 72755452Sbostic hashp->cbucket = bucket; 72855452Sbostic if (hashp->cbucket > hashp->MAX_BUCKET) { 72955452Sbostic hashp->cbucket = -1; 73055452Sbostic return (ABNORMAL); 73155452Sbostic } 73255452Sbostic } else 73355452Sbostic bp = (u_short *)hashp->cpage->page; 73455452Sbostic 73555452Sbostic #ifdef DEBUG 73655452Sbostic assert(bp); 73755452Sbostic assert(bufp); 73855452Sbostic #endif 73955452Sbostic while (bp[hashp->cndx + 1] == OVFLPAGE) { 74055452Sbostic bufp = hashp->cpage = 74157586Sbostic __get_buf(hashp, bp[hashp->cndx], bufp, 0); 74250997Sbostic if (!bufp) 74350997Sbostic return (ERROR); 74455452Sbostic bp = (u_short *)(bufp->page); 74555452Sbostic hashp->cndx = 1; 74650997Sbostic } 74755874Sbostic if (!bp[0]) { 74855452Sbostic hashp->cpage = NULL; 74955874Sbostic ++hashp->cbucket; 75055874Sbostic } 75146366Sbostic } 75246366Sbostic ndx = hashp->cndx; 75350997Sbostic if (bp[ndx + 1] < REAL_KEY) { 75457586Sbostic if (__big_keydata(hashp, bufp, key, data, 1)) 75550997Sbostic return (ERROR); 75646366Sbostic } else { 75750997Sbostic key->data = (u_char *)hashp->cpage->page + bp[ndx]; 75850997Sbostic key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx]; 75950997Sbostic data->data = (u_char *)hashp->cpage->page + bp[ndx + 1]; 76050997Sbostic data->size = bp[ndx] - bp[ndx + 1]; 76150997Sbostic ndx += 2; 76250997Sbostic if (ndx > bp[0]) { 76350997Sbostic hashp->cpage = NULL; 76450997Sbostic hashp->cbucket++; 76550997Sbostic hashp->cndx = 1; 76650997Sbostic } else 76750997Sbostic hashp->cndx = ndx; 76846366Sbostic } 76946366Sbostic return (SUCCESS); 77046366Sbostic } 77146366Sbostic 77246366Sbostic /********************************* UTILITIES ************************/ 77350997Sbostic 77446366Sbostic /* 77550997Sbostic * Returns: 77650997Sbostic * 0 ==> OK 77750997Sbostic * -1 ==> Error 77850997Sbostic */ 77946366Sbostic extern int 78057586Sbostic __expand_table(hashp) 78157586Sbostic HTAB *hashp; 78246366Sbostic { 78350997Sbostic u_int old_bucket, new_bucket; 78450997Sbostic int dirsize, new_segnum, spare_ndx; 78550997Sbostic 78646366Sbostic #ifdef HASH_STATISTICS 78746366Sbostic hash_expansions++; 78846366Sbostic #endif 78946366Sbostic new_bucket = ++hashp->MAX_BUCKET; 79046366Sbostic old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK); 79146366Sbostic 79246366Sbostic new_segnum = new_bucket >> hashp->SSHIFT; 79346366Sbostic 79446366Sbostic /* Check if we need a new segment */ 79550997Sbostic if (new_segnum >= hashp->nsegs) { 79650997Sbostic /* Check if we need to expand directory */ 79750997Sbostic if (new_segnum >= hashp->DSIZE) { 79850997Sbostic /* Reallocate directory */ 79950997Sbostic dirsize = hashp->DSIZE * sizeof(SEGMENT *); 80050997Sbostic if (!hash_realloc(&hashp->dir, dirsize, dirsize << 1)) 80150997Sbostic return (-1); 80250997Sbostic hashp->DSIZE = dirsize << 1; 80346366Sbostic } 80450997Sbostic if (!(hashp->dir[new_segnum] = 80550997Sbostic calloc(hashp->SGSIZE, sizeof(SEGMENT)))) 80650997Sbostic return (-1); 80750997Sbostic hashp->exsegs++; 80850997Sbostic hashp->nsegs++; 80946366Sbostic } 81046366Sbostic /* 81150997Sbostic * If the split point is increasing (MAX_BUCKET's log base 2 81250997Sbostic * * increases), we need to copy the current contents of the spare 81350997Sbostic * split bucket to the next bucket. 81450997Sbostic */ 81552001Sbostic spare_ndx = __log2(hashp->MAX_BUCKET + 1); 81652001Sbostic if (spare_ndx > hashp->OVFL_POINT) { 81751061Sbostic hashp->SPARES[spare_ndx] = hashp->SPARES[hashp->OVFL_POINT]; 81851061Sbostic hashp->OVFL_POINT = spare_ndx; 81951061Sbostic } 82051061Sbostic 82150997Sbostic if (new_bucket > hashp->HIGH_MASK) { 82250997Sbostic /* Starting a new doubling */ 82350997Sbostic hashp->LOW_MASK = hashp->HIGH_MASK; 82450997Sbostic hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK; 82546366Sbostic } 82650997Sbostic /* Relocate records to the new bucket */ 82757586Sbostic return (__split_page(hashp, old_bucket, new_bucket)); 82850997Sbostic } 82946366Sbostic 83046366Sbostic /* 83150997Sbostic * If realloc guarantees that the pointer is not destroyed if the realloc 83250997Sbostic * fails, then this routine can go away. 83350997Sbostic */ 83450997Sbostic static void * 83550997Sbostic hash_realloc(p_ptr, oldsize, newsize) 83650997Sbostic SEGMENT **p_ptr; 83750997Sbostic int oldsize, newsize; 83846366Sbostic { 83950997Sbostic register void *p; 84046366Sbostic 84150997Sbostic if (p = malloc(newsize)) { 84258016Sbostic memmove(p, *p_ptr, oldsize); 84363747Sbostic memset((char *)p + oldsize, 0, newsize - oldsize); 84446366Sbostic free(*p_ptr); 84546366Sbostic *p_ptr = p; 84646366Sbostic } 84746366Sbostic return (p); 84846366Sbostic } 84946366Sbostic 85047251Sbostic extern u_int 85157586Sbostic __call_hash(hashp, k, len) 85257586Sbostic HTAB *hashp; 85350997Sbostic char *k; 85450997Sbostic int len; 85546366Sbostic { 85650997Sbostic int n, bucket; 85750997Sbostic 85846366Sbostic n = hashp->hash(k, len); 85946366Sbostic bucket = n & hashp->HIGH_MASK; 86050997Sbostic if (bucket > hashp->MAX_BUCKET) 86150997Sbostic bucket = bucket & hashp->LOW_MASK; 86250997Sbostic return (bucket); 86346366Sbostic } 86446366Sbostic 86546366Sbostic /* 86650997Sbostic * Allocate segment table. On error, destroy the table and set errno. 86750997Sbostic * 86850997Sbostic * Returns 0 on success 86950997Sbostic */ 87046366Sbostic static int 87157586Sbostic alloc_segs(hashp, nsegs) 87257586Sbostic HTAB *hashp; 87350997Sbostic int nsegs; 87446366Sbostic { 87550997Sbostic register int i; 87650997Sbostic register SEGMENT store; 87746366Sbostic 87850997Sbostic int save_errno; 87946366Sbostic 88050997Sbostic if (!(hashp->dir = calloc(hashp->DSIZE, sizeof(SEGMENT *)))) { 88150997Sbostic save_errno = errno; 88257586Sbostic (void)hdestroy(hashp); 88350997Sbostic errno = save_errno; 88450997Sbostic return (-1); 88550997Sbostic } 88650997Sbostic /* Allocate segments */ 88750997Sbostic store = calloc(nsegs << hashp->SSHIFT, sizeof(SEGMENT)); 88850997Sbostic if (!store) { 88950997Sbostic save_errno = errno; 89057586Sbostic (void)hdestroy(hashp); 89150997Sbostic errno = save_errno; 89250997Sbostic return (-1); 89350997Sbostic } 89450997Sbostic for (i = 0; i < nsegs; i++, hashp->nsegs++) 89550997Sbostic hashp->dir[i] = &store[i << hashp->SSHIFT]; 89650997Sbostic return (0); 89746366Sbostic } 89846366Sbostic 89950997Sbostic #if BYTE_ORDER == LITTLE_ENDIAN 90046366Sbostic /* 90150997Sbostic * Hashp->hdr needs to be byteswapped. 90250997Sbostic */ 90346366Sbostic static void 90450997Sbostic swap_header_copy(srcp, destp) 90550997Sbostic HASHHDR *srcp, *destp; 90646366Sbostic { 90750997Sbostic int i; 90846366Sbostic 90950997Sbostic BLSWAP_COPY(srcp->magic, destp->magic); 91050997Sbostic BLSWAP_COPY(srcp->version, destp->version); 91150997Sbostic BLSWAP_COPY(srcp->lorder, destp->lorder); 91250997Sbostic BLSWAP_COPY(srcp->bsize, destp->bsize); 91350997Sbostic BLSWAP_COPY(srcp->bshift, destp->bshift); 91450997Sbostic BLSWAP_COPY(srcp->dsize, destp->dsize); 91550997Sbostic BLSWAP_COPY(srcp->ssize, destp->ssize); 91650997Sbostic BLSWAP_COPY(srcp->sshift, destp->sshift); 91751061Sbostic BLSWAP_COPY(srcp->ovfl_point, destp->ovfl_point); 91851061Sbostic BLSWAP_COPY(srcp->last_freed, destp->last_freed); 91950997Sbostic BLSWAP_COPY(srcp->max_bucket, destp->max_bucket); 92050997Sbostic BLSWAP_COPY(srcp->high_mask, destp->high_mask); 92150997Sbostic BLSWAP_COPY(srcp->low_mask, destp->low_mask); 92250997Sbostic BLSWAP_COPY(srcp->ffactor, destp->ffactor); 92350997Sbostic BLSWAP_COPY(srcp->nkeys, destp->nkeys); 92450997Sbostic BLSWAP_COPY(srcp->hdrpages, destp->hdrpages); 92550997Sbostic BLSWAP_COPY(srcp->h_charkey, destp->h_charkey); 92650997Sbostic for (i = 0; i < NCACHED; i++) { 92750997Sbostic BLSWAP_COPY(srcp->spares[i], destp->spares[i]); 92850997Sbostic BSSWAP_COPY(srcp->bitmaps[i], destp->bitmaps[i]); 92950997Sbostic } 93046366Sbostic } 93146366Sbostic 93246366Sbostic static void 93357586Sbostic swap_header(hashp) 93457586Sbostic HTAB *hashp; 93546366Sbostic { 93650997Sbostic HASHHDR *hdrp; 93750997Sbostic int i; 93846366Sbostic 93950997Sbostic hdrp = &hashp->hdr; 94046366Sbostic 94150997Sbostic BLSWAP(hdrp->magic); 94250997Sbostic BLSWAP(hdrp->version); 94350997Sbostic BLSWAP(hdrp->lorder); 94450997Sbostic BLSWAP(hdrp->bsize); 94550997Sbostic BLSWAP(hdrp->bshift); 94650997Sbostic BLSWAP(hdrp->dsize); 94750997Sbostic BLSWAP(hdrp->ssize); 94850997Sbostic BLSWAP(hdrp->sshift); 94951061Sbostic BLSWAP(hdrp->ovfl_point); 95051061Sbostic BLSWAP(hdrp->last_freed); 95150997Sbostic BLSWAP(hdrp->max_bucket); 95250997Sbostic BLSWAP(hdrp->high_mask); 95350997Sbostic BLSWAP(hdrp->low_mask); 95450997Sbostic BLSWAP(hdrp->ffactor); 95550997Sbostic BLSWAP(hdrp->nkeys); 95650997Sbostic BLSWAP(hdrp->hdrpages); 95750997Sbostic BLSWAP(hdrp->h_charkey); 95850997Sbostic for (i = 0; i < NCACHED; i++) { 95950997Sbostic BLSWAP(hdrp->spares[i]); 96050997Sbostic BSSWAP(hdrp->bitmaps[i]); 96150997Sbostic } 96246366Sbostic } 96350997Sbostic #endif 964