146366Sbostic /*- 246366Sbostic * Copyright (c) 1990 The Regents of the University of California. 346366Sbostic * All rights reserved. 446366Sbostic * 546366Sbostic * This code is derived from software contributed to Berkeley by 646366Sbostic * Margo Seltzer. 746366Sbostic * 846366Sbostic * %sccs.include.redist.c% 946366Sbostic */ 1046366Sbostic 1146366Sbostic #if defined(LIBC_SCCS) && !defined(lint) 12*47251Sbostic static char sccsid[] = "@(#)hash.c 5.13 (Berkeley) 03/12/91"; 1346366Sbostic #endif /* LIBC_SCCS and not lint */ 1446366Sbostic 1546366Sbostic #include <sys/param.h> 1646366Sbostic #include <sys/stat.h> 1746562Sbostic #include <fcntl.h> 1846366Sbostic #include <errno.h> 1946366Sbostic #include <assert.h> 2046366Sbostic #include <db.h> 2146562Sbostic #include <stdio.h> 2246562Sbostic #include <stdlib.h> 2346500Sbostic #include <unistd.h> 2446562Sbostic #include <string.h> 2546366Sbostic #include "hash.h" 2646366Sbostic 2746366Sbostic /* Fast arithmetic, relying on powers of 2, */ 2846366Sbostic 2946366Sbostic #define MOD(x,y) ((x) & ((y)-1)) 3046366Sbostic #define RETURN_ERROR(ERR,LOC) { save_errno = ERR; goto LOC; } 3146366Sbostic 3246366Sbostic /* Return values */ 3346366Sbostic 3446366Sbostic #define SUCCESS 0 3546366Sbostic #define ERROR -1 3646366Sbostic #define ABNORMAL 1 3746366Sbostic 3846366Sbostic /* page.c */ 3946366Sbostic extern int __get_page(); 4046366Sbostic extern int __split_page(); 4146366Sbostic extern int __addel(); 4246366Sbostic extern int __delpair(); 4346366Sbostic extern u_long *__init_bitmap(); 4446366Sbostic 4546366Sbostic /* big.c */ 46*47251Sbostic extern int __big_return(); 4746366Sbostic extern int __big_keydata(); 4846366Sbostic extern u_short __find_last_page(); 4946366Sbostic 5046366Sbostic /* buf.c */ 5146366Sbostic extern BUFHEAD *__get_buf(); 5246366Sbostic extern void __buf_init(); 5346366Sbostic extern int __buf_free(); 5446366Sbostic 5546366Sbostic /* hash function */ 5646366Sbostic extern int (*default_hash)(); 5746366Sbostic 5846366Sbostic /* My externals */ 5946366Sbostic extern int __expand_table(); 60*47251Sbostic extern u_int __call_hash(); 6146366Sbostic 6246366Sbostic /* Internal routines */ 6346366Sbostic static HTAB *init_hash(); 6446366Sbostic static int hash_access(); 6546366Sbostic static int flush_meta(); 6646366Sbostic static int alloc_segs(); 6746366Sbostic static int init_htab(); 6846366Sbostic static char *hash_realloc(); 6946366Sbostic static int hdestroy(); 7046366Sbostic static int hash_put(); 7146366Sbostic static int hash_delete(); 7246366Sbostic static int hash_get(); 7346366Sbostic static int hash_sync(); 7446366Sbostic static int hash_close(); 7546366Sbostic static int hash_seq(); 7646366Sbostic static void swap_header_copy(); 7746366Sbostic static void swap_header(); 7846366Sbostic 7946366Sbostic /* Local data */ 8046366Sbostic 8146366Sbostic HTAB *hashp = NULL; 8246366Sbostic 8346366Sbostic #ifdef HASH_STATISTICS 8446366Sbostic long hash_accesses, hash_collisions, hash_expansions, hash_overflows; 8546366Sbostic #endif 8646366Sbostic 8746366Sbostic /************************** INTERFACE ROUTINES **********************/ 8846366Sbostic /* OPEN/CLOSE */ 8946366Sbostic 9046366Sbostic extern DB * 9146366Sbostic hash_open ( file, flags, mode, info ) 9246534Sbostic const char *file; 9346366Sbostic int flags; 9446366Sbostic int mode; 9546534Sbostic const HASHINFO *info; /* Special directives for create */ 9646366Sbostic { 9746366Sbostic int buckets; 9846366Sbostic int bpages; 9946366Sbostic int hdrsize; 10046366Sbostic int i; 10146366Sbostic int new_table = 0; 10246366Sbostic int nkey; 10346366Sbostic int nsegs; 10446366Sbostic int save_errno; 10546366Sbostic struct stat statbuf; 10646366Sbostic DB *dbp; 10746366Sbostic 10846366Sbostic 10946366Sbostic if ( !(hashp = (HTAB *) calloc( 1, sizeof(HTAB) ))) { 11046366Sbostic /* calloc should set errno */ 11146366Sbostic return(0); 11246366Sbostic } 11346457Sbostic hashp->fp = -1; 11446366Sbostic /* 11546366Sbostic Select flags relevant to us. 11646366Sbostic Even if user wants write only, we need to be able to read 11746366Sbostic the actual file, so we need to open it read/write. But, the 11846366Sbostic field in the hashp structure needs to be accurate so that 11946366Sbostic we can check accesses. 12046366Sbostic */ 12146366Sbostic flags = flags & (O_CREAT | O_EXCL | O_RDONLY | O_RDWR | O_TRUNC | O_WRONLY); 12246366Sbostic hashp->flags = flags; 12346366Sbostic if ( flags & O_WRONLY ) flags = (flags & ~O_WRONLY) | O_RDWR; 12446366Sbostic 12546366Sbostic if ( !file || 12646366Sbostic (flags & O_TRUNC) || 12746366Sbostic (stat ( file, &statbuf ) && (errno == ENOENT)) ) { 12846503Sbostic if ( errno == ENOENT ) { 12946503Sbostic errno = 0; /* Just in case someone looks at errno */ 13046503Sbostic } 13146366Sbostic new_table = 1; 13246366Sbostic } 13346366Sbostic 13446485Sbostic if ( file ) { 13546485Sbostic if ((hashp->fp = open ( file, flags, mode )) == -1) { 13646485Sbostic RETURN_ERROR (errno, error0); 13746485Sbostic } 13846485Sbostic (void)fcntl(hashp->fp, F_SETFD, 1); 13946366Sbostic } 14046366Sbostic 14146366Sbostic if ( new_table ) { 14246366Sbostic if ( !(hashp = init_hash( info )) ) { 14346366Sbostic RETURN_ERROR(errno,error1); 14446366Sbostic } 14546366Sbostic } else { 14646366Sbostic /* Table already exists */ 14746366Sbostic if ( info && info->hash ) hashp->hash = info->hash; 14846366Sbostic else hashp->hash = default_hash; 14946366Sbostic 15046366Sbostic hdrsize = read ( hashp->fp, &hashp->hdr, sizeof(HASHHDR) ); 15146366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN 15246366Sbostic swap_header ( ); 15346366Sbostic #endif 15446366Sbostic if ( hdrsize == -1 ) { 15546366Sbostic RETURN_ERROR(errno, error1); 15646366Sbostic } 15746366Sbostic if ( hdrsize != sizeof(HASHHDR) ) { 15846366Sbostic RETURN_ERROR(EFTYPE, error1); 15946366Sbostic } 16046366Sbostic 16146366Sbostic /* Verify file type, versions and hash function */ 16246366Sbostic if ( hashp->MAGIC != HASHMAGIC ) 16346366Sbostic RETURN_ERROR ( EFTYPE, error1 ); 16446366Sbostic if ( hashp->VERSION != VERSION_NO ) 16546366Sbostic RETURN_ERROR ( EFTYPE, error1 ); 16646366Sbostic if (hashp->hash ( CHARKEY, sizeof(CHARKEY) ) != hashp->H_CHARKEY ) { 16746366Sbostic RETURN_ERROR ( EFTYPE, error1 ); 16846366Sbostic } 16946366Sbostic 17046366Sbostic /* 17146366Sbostic Figure out how many segments we need. 17246366Sbostic */ 17346366Sbostic nsegs = (hashp->MAX_BUCKET + hashp->SGSIZE -1)/ hashp->SGSIZE; 17446366Sbostic hashp->nsegs = 0; 17546366Sbostic if (alloc_segs( nsegs )) { 17646366Sbostic /* 17746366Sbostic If alloc_segs fails, table will have been destroyed 17846366Sbostic and errno will have been set. 17946366Sbostic */ 18046366Sbostic return( (DB *) NULL ); 18146366Sbostic } 18246366Sbostic 18346366Sbostic /* Read in bitmaps */ 18446366Sbostic bpages = (hashp->SPARES[__log2(hashp->MAX_BUCKET)] + 18546366Sbostic (hashp->BSIZE << BYTE_SHIFT) - 1) >> 18646366Sbostic (hashp->BSHIFT + BYTE_SHIFT); 18746366Sbostic 18846503Sbostic hashp->nmaps = bpages; 189*47251Sbostic memset ( &hashp->mapp[0], 0, bpages * sizeof ( u_long *) ); 19046366Sbostic } 19146366Sbostic 19246366Sbostic /* Initialize Buffer Manager */ 193*47251Sbostic if ( info && info->cachesize ) { 194*47251Sbostic __buf_init (info->cachesize); 19546366Sbostic } else { 19646366Sbostic __buf_init (DEF_BUFSIZE); 19746366Sbostic } 19846366Sbostic 19946366Sbostic hashp->new_file = new_table; 200*47251Sbostic hashp->save_file = file && (hashp->flags & (O_WRONLY|O_RDWR)); 20146366Sbostic hashp->cbucket = -1; 20246366Sbostic if ( !(dbp = (DB *)malloc ( sizeof (DB) )) ) { 20346366Sbostic save_errno = errno; 20446366Sbostic hdestroy(); 20546366Sbostic errno = save_errno; 20646366Sbostic return ( NULL ); 20746366Sbostic } 20846366Sbostic dbp->internal = (char *)hashp; 20946366Sbostic dbp->close = hash_close; 21046534Sbostic dbp->del = hash_delete; 21146366Sbostic dbp->get = hash_get; 21246366Sbostic dbp->put = hash_put; 21346366Sbostic dbp->seq = hash_seq; 21446366Sbostic dbp->sync = hash_sync; 21546366Sbostic 21646366Sbostic #ifdef DEBUG 21746366Sbostic (void)fprintf(stderr, "%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%x\n%s%x\n%s%d\n%s%d\n", 21846366Sbostic "init_htab:", 21946366Sbostic "TABLE POINTER ", hashp, 22046366Sbostic "BUCKET SIZE ", hashp->BSIZE, 22146366Sbostic "BUCKET SHIFT ", hashp->BSHIFT, 22246366Sbostic "DIRECTORY SIZE ", hashp->DSIZE, 22346366Sbostic "SEGMENT SIZE ", hashp->SGSIZE, 22446366Sbostic "SEGMENT SHIFT ", hashp->SSHIFT, 22546366Sbostic "FILL FACTOR ", hashp->FFACTOR, 22646366Sbostic "MAX BUCKET ", hashp->MAX_BUCKET, 22746366Sbostic "HIGH MASK ", hashp->HIGH_MASK, 22846366Sbostic "LOW MASK ", hashp->LOW_MASK, 22946366Sbostic "NSEGS ", hashp->nsegs, 23046366Sbostic "NKEYS ", hashp->NKEYS ); 23146366Sbostic #endif 23246366Sbostic #ifdef HASH_STATISTICS 23346366Sbostic hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0; 23446366Sbostic #endif 23546366Sbostic return (dbp); 23646366Sbostic 23746366Sbostic error2: 23846366Sbostic (void)hdestroy(); 23946366Sbostic errno = save_errno; 24046366Sbostic return ( (DB *)NULL ); 24146366Sbostic 24246366Sbostic error1: 24346366Sbostic (void) close ( hashp->fp ); 24446366Sbostic 24546366Sbostic error0: 24646366Sbostic free ( hashp ); 24746366Sbostic errno = save_errno; 24846366Sbostic return ( (DB *) NULL ); 24946366Sbostic } 25046366Sbostic 25146366Sbostic static int 25246366Sbostic hash_close (dbp) 25346366Sbostic DB *dbp; 25446366Sbostic { 25546457Sbostic int retval; 25646457Sbostic 25746366Sbostic if ( !dbp ) { 25846366Sbostic return(ERROR); 25946366Sbostic } 26046366Sbostic hashp = (HTAB *)dbp->internal; 26146457Sbostic retval = hdestroy(); 26246457Sbostic (void)free ( dbp ); 26346457Sbostic return ( retval ); 26446366Sbostic } 26546366Sbostic 26646366Sbostic 26746366Sbostic /************************** LOCAL CREATION ROUTINES **********************/ 26846366Sbostic static HTAB * 26946366Sbostic init_hash( info ) 27046366Sbostic HASHINFO *info; 27146366Sbostic { 27246366Sbostic int nelem; 27346366Sbostic 27446366Sbostic nelem = 1; 27546366Sbostic 27646366Sbostic hashp->LORDER = BYTE_ORDER; 27746366Sbostic hashp->BSIZE = DEF_BUCKET_SIZE; 27846366Sbostic hashp->BSHIFT = DEF_BUCKET_SHIFT; 27946366Sbostic hashp->SGSIZE = DEF_SEGSIZE; 28046366Sbostic hashp->SSHIFT = DEF_SEGSIZE_SHIFT; 28146366Sbostic hashp->DSIZE = DEF_DIRSIZE; 28246366Sbostic hashp->FFACTOR = DEF_FFACTOR; 28346366Sbostic hashp->hash = default_hash; 28446366Sbostic bzero (hashp->SPARES, sizeof (hashp->SPARES)); 28546366Sbostic 28646366Sbostic if ( info ) { 28746366Sbostic if ( info->bsize ) { 28846366Sbostic /* Round pagesize up to power of 2 */ 28946366Sbostic hashp->BSHIFT = __log2(info->bsize); 29046366Sbostic hashp->BSIZE = 1 << hashp->BSHIFT; 29146366Sbostic if ( hashp->BSIZE > MAX_BSIZE ) { 29246366Sbostic errno = EINVAL; 29346366Sbostic return(0); 29446366Sbostic } 29546366Sbostic } 29646366Sbostic if ( info->ffactor ) hashp->FFACTOR = info->ffactor; 29746366Sbostic if ( info->hash ) hashp->hash = info->hash; 29846366Sbostic if ( info->nelem ) nelem = info->nelem; 29946366Sbostic if ( info->lorder ) { 30046366Sbostic if ( info->lorder != BIG_ENDIAN && 30146366Sbostic info->lorder != LITTLE_ENDIAN) { 30246366Sbostic errno = EINVAL; 30346366Sbostic return(0); 30446366Sbostic } 30546366Sbostic hashp->LORDER = info->lorder; 30646366Sbostic } 30746366Sbostic } 30846366Sbostic 30946366Sbostic /* init_htab should destroy the table and set errno if it fails */ 31046366Sbostic if ( init_htab ( nelem ) ) { 31146366Sbostic return(0); 31246366Sbostic } else { 31346366Sbostic return(hashp); 31446366Sbostic } 31546366Sbostic } 31646366Sbostic 31746366Sbostic /* 31846366Sbostic This calls alloc_segs which may run out of memory. 31946366Sbostic Alloc_segs will destroy the table and set errno, 32046366Sbostic so we just pass the error information along. 32146366Sbostic 32246366Sbostic Returns 0 on No Error 32346366Sbostic 32446366Sbostic */ 32546366Sbostic static int 32646366Sbostic init_htab ( nelem ) 32746366Sbostic int nelem; 32846366Sbostic { 32946366Sbostic register SEGMENT *segp; 33046366Sbostic register int nbuckets; 33146366Sbostic register int nsegs; 33246366Sbostic int l2; 33346366Sbostic 33446366Sbostic /* 33546366Sbostic * Divide number of elements by the fill factor and determine a desired 33646366Sbostic * number of buckets. Allocate space for the next greater power of 33746366Sbostic * two number of buckets 33846366Sbostic */ 33946366Sbostic nelem = (nelem - 1) / hashp->FFACTOR + 1; 34046366Sbostic 34146366Sbostic l2 = __log2(nelem); 34246366Sbostic nbuckets = 1 << l2; 34346366Sbostic nbuckets = MAX ( nbuckets, 2 ); 34446366Sbostic 34546366Sbostic hashp->SPARES[l2] = l2 + 1; 34646366Sbostic hashp->SPARES[l2+1] = l2 + 1; 34746366Sbostic /* 34846366Sbostic First bitmap page is at: 34946366Sbostic splitpoint l2 35046366Sbostic page offset 1 35146366Sbostic */ 35246366Sbostic __init_bitmap ( OADDR_OF(l2,1), l2+1, 0 ); 35346366Sbostic 35446366Sbostic hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1; 35546366Sbostic hashp->HIGH_MASK = (nbuckets << 1) - 1; 35646366Sbostic hashp->HDRPAGES = ((MAX(sizeof(HASHHDR),MINHDRSIZE) - 1) >> 35746366Sbostic hashp->BSHIFT) + 1; 35846366Sbostic 35946366Sbostic nsegs = (nbuckets - 1) / hashp->SGSIZE + 1; 36046366Sbostic nsegs = 1 << __log2(nsegs); 36146366Sbostic 36246366Sbostic if ( nsegs > hashp->DSIZE ) { 36346366Sbostic hashp->DSIZE = nsegs; 36446366Sbostic } 36546366Sbostic 36646366Sbostic return (alloc_segs ( nsegs ) ); 36746366Sbostic } 36846366Sbostic 36946366Sbostic /********************** DESTROY/CLOSE ROUTINES ************************/ 37046366Sbostic 37146366Sbostic /* 37246366Sbostic Flushes any changes to the file if necessary and 37346366Sbostic destroys the hashp structure, freeing all allocated 37446366Sbostic space. 37546366Sbostic */ 37646366Sbostic static int 37746366Sbostic hdestroy() 37846366Sbostic { 37946366Sbostic int save_errno; 38046366Sbostic int i; 38146366Sbostic 38246366Sbostic save_errno = 0; 38346366Sbostic 38446366Sbostic if (hashp != NULL) { 38546366Sbostic #ifdef HASH_STATISTICS 38646366Sbostic (void) fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n", 38746366Sbostic hash_accesses, hash_collisions); 38846366Sbostic (void) fprintf(stderr, "hdestroy: expansions %ld\n", 38946366Sbostic hash_expansions); 39046366Sbostic (void) fprintf(stderr, "hdestroy: overflows %ld\n", 39146366Sbostic hash_overflows); 39246366Sbostic (void) fprintf(stderr, "keys %ld maxp %d segmentcount %d\n", 39346366Sbostic hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs); 39446366Sbostic 39546366Sbostic for ( i = 0; i < NCACHED; i++ ) { 39646366Sbostic (void) fprintf ( stderr, "spares[%d] = %d\n", i, hashp->SPARES[i] ); 39746366Sbostic } 39846366Sbostic #endif 39946366Sbostic 40046366Sbostic /* 40146366Sbostic Call on buffer manager to free buffers, and if 40246366Sbostic required, write them to disk 40346366Sbostic */ 40446366Sbostic if (__buf_free(1, hashp->save_file)) { 40546366Sbostic save_errno = errno; 40646366Sbostic } 40746366Sbostic if ( hashp->dir ) { 40846366Sbostic (void)free(*hashp->dir); /* Free initial segments */ 40946366Sbostic /* Free extra segments */ 41046366Sbostic while ( hashp->exsegs-- ) { 41146366Sbostic (void)free ( hashp->dir[--hashp->nsegs] ); 41246366Sbostic } 41346366Sbostic (void)free(hashp->dir); 41446366Sbostic } 41546366Sbostic if (flush_meta() && !save_errno) { 41646366Sbostic save_errno = errno; 41746366Sbostic } 41846503Sbostic 419*47251Sbostic /* Free Bigmaps */ 420*47251Sbostic for ( i = 0; i < hashp->nmaps; i++ ) { 421*47251Sbostic if ( hashp->mapp[i] ) { 422*47251Sbostic (void) free ( hashp->mapp[i] ); 423*47251Sbostic } 42446503Sbostic } 42546503Sbostic 42646457Sbostic if ( hashp->fp != -1 ) { 42746457Sbostic (void)close (hashp->fp); 42846457Sbostic } 42946366Sbostic (void)free(hashp); 43046366Sbostic hashp = NULL; 43146366Sbostic } 43246366Sbostic if ( save_errno ) { 43346366Sbostic errno = save_errno; 43446366Sbostic return(ERROR); 43546366Sbostic } else { 43646366Sbostic return(SUCCESS); 43746366Sbostic } 43846366Sbostic } 43946366Sbostic 44046366Sbostic /* 44146366Sbostic Write modified pages to disk 44246366Sbostic 0 == OK 44346366Sbostic -1 ERROR 44446366Sbostic */ 44546366Sbostic static int 44646366Sbostic hash_sync(dbp) 44746366Sbostic DB *dbp; 44846366Sbostic { 44946366Sbostic if ( !dbp ) { 45046366Sbostic return (ERROR); 45146366Sbostic } 45246366Sbostic 45346366Sbostic hashp = (HTAB *)dbp->internal; 45446366Sbostic 45546366Sbostic if (!hashp->save_file) return(0); 45646366Sbostic if ( __buf_free ( 0, 1 ) || flush_meta()) { 45746366Sbostic return(ERROR); 45846366Sbostic } 45946366Sbostic hashp->new_file = 0; 46046366Sbostic return(0); 46146366Sbostic } 46246366Sbostic 46346366Sbostic /* 46446366Sbostic 0 == OK 46546366Sbostic -1 indicates that errno should be set 46646366Sbostic */ 46746366Sbostic static int 46846366Sbostic flush_meta() 46946366Sbostic { 47046366Sbostic int fp; 47146366Sbostic int hdrsize; 47246366Sbostic int i; 47346366Sbostic int wsize; 47446366Sbostic HASHHDR *whdrp; 47546366Sbostic HASHHDR whdr; 47646366Sbostic 47746366Sbostic if (!hashp->save_file) return(0); 47846366Sbostic hashp->MAGIC = HASHMAGIC; 47946366Sbostic hashp->VERSION = VERSION_NO; 48046366Sbostic hashp->H_CHARKEY = hashp->hash ( CHARKEY, sizeof(CHARKEY)); 48146366Sbostic 48246366Sbostic fp = hashp->fp; 48346366Sbostic whdrp = &hashp->hdr; 48446366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN 48546366Sbostic whdrp = &whdr; 48646366Sbostic swap_header_copy( &hashp->hdr, whdrp ); 48746366Sbostic #endif 48846500Sbostic if ( (lseek (fp, 0, SEEK_SET) == -1 ) || 48946366Sbostic ((wsize = write ( fp, whdrp, sizeof(HASHHDR))) == -1)) { 49046366Sbostic return(-1); 49146366Sbostic } else if ( wsize != sizeof(HASHHDR) ) { 49246366Sbostic errno = EFTYPE; 49346366Sbostic hashp->errno = errno; 49446366Sbostic return(-1); 49546366Sbostic } 49646366Sbostic for ( i = 0; i < NCACHED; i++ ) { 49746366Sbostic if ( hashp->mapp[i] ) { 49846366Sbostic if (!__put_page((char *)hashp->mapp[i], 49946366Sbostic hashp->BITMAPS[i], 0, 1)){ 50046366Sbostic return(-1); 50146366Sbostic } 50246366Sbostic } 50346366Sbostic } 50446366Sbostic return(0); 50546366Sbostic } 50646366Sbostic /*******************************SEARCH ROUTINES *****************************/ 50746366Sbostic /* 50846366Sbostic All the access routines return 50946366Sbostic 0 on SUCCESS 51046366Sbostic 1 to indicate an external ERROR (i.e. key not found, etc) 51146366Sbostic -1 to indicate an internal ERROR (i.e. out of memory, etc) 51246366Sbostic */ 51346366Sbostic static int 51446571Sbostic hash_get ( dbp, key, data, flag ) 51546366Sbostic DB *dbp; 51646571Sbostic DBT *key, *data; 517*47251Sbostic u_int flag; 51846366Sbostic { 51946571Sbostic #ifdef lint 52046571Sbostic flag = flag; 52146571Sbostic #endif 52246571Sbostic 52346366Sbostic if ( !dbp ) { 52446366Sbostic return (ERROR); 52546366Sbostic } 52646366Sbostic hashp = (HTAB *)dbp->internal; 52746366Sbostic if ( hashp->flags & O_WRONLY ) { 52846366Sbostic errno = EBADF; 52946366Sbostic hashp->errno = errno; 53046366Sbostic return ( ERROR ); 53146366Sbostic } 53246366Sbostic return ( hash_access ( HASH_GET, key, data ) ); 53346366Sbostic } 53446366Sbostic 53546366Sbostic static int 53646366Sbostic hash_put ( dbp, key, data, flag ) 53746366Sbostic DB *dbp; 53846366Sbostic DBT *key, *data; 539*47251Sbostic u_int flag; 54046366Sbostic { 54146366Sbostic if ( !dbp ) { 54246366Sbostic return (ERROR); 54346366Sbostic } 54446366Sbostic hashp = (HTAB *)dbp->internal; 54546366Sbostic if ( (hashp->flags & O_ACCMODE) == O_RDONLY ) { 54646366Sbostic errno = EBADF; 54746366Sbostic hashp->errno = errno; 54846366Sbostic return ( ERROR ); 54946366Sbostic } 55046366Sbostic if ( flag == R_NOOVERWRITE ) { 55146366Sbostic return ( hash_access ( HASH_PUTNEW, key, data ) ); 55246366Sbostic } else { 55346366Sbostic return ( hash_access ( HASH_PUT, key, data ) ); 55446366Sbostic } 55546366Sbostic } 55646366Sbostic 55746366Sbostic static int 55846571Sbostic hash_delete ( dbp, key, flag ) 55946366Sbostic DB *dbp; 56046366Sbostic DBT *key; 561*47251Sbostic u_int flag; /* Ignored */ 56246366Sbostic { 56346571Sbostic #ifdef lint 56446571Sbostic flag = flag; 56546571Sbostic #endif 56646366Sbostic if ( !dbp ) { 56746366Sbostic return (ERROR); 56846366Sbostic } 56946366Sbostic hashp = (HTAB *)dbp->internal; 57046366Sbostic if ( (hashp->flags & O_ACCMODE) == O_RDONLY ) { 57146366Sbostic errno = EBADF; 57246366Sbostic hashp->errno = errno; 57346366Sbostic return ( ERROR ); 57446366Sbostic } 57546366Sbostic return ( hash_access ( HASH_DELETE, key, NULL ) ); 57646366Sbostic } 57746366Sbostic 57846366Sbostic /* 57946366Sbostic Assume that hashp has been set in wrapper routine 58046366Sbostic */ 58146366Sbostic static int 58246366Sbostic hash_access(action, key, val) 58346366Sbostic ACTION action; 58446366Sbostic DBT *key, *val; 58546366Sbostic { 58646366Sbostic register BUFHEAD *rbufp; 58746366Sbostic register u_short *bp; 58846366Sbostic register int ndx; 58946366Sbostic register int n; 59046366Sbostic register int off = hashp->BSIZE; 59146366Sbostic register int size; 59246366Sbostic register char *kp; 59346366Sbostic BUFHEAD *bufp; 59446457Sbostic BUFHEAD *save_bufp; 59546366Sbostic u_short pageno; 59646366Sbostic 59746366Sbostic #ifdef HASH_STATISTICS 59846366Sbostic hash_accesses++; 59946366Sbostic #endif 60046366Sbostic 60146366Sbostic size = key->size; 60246950Sbostic kp = (char *)key->data; 60346457Sbostic rbufp = __get_buf ( __call_hash(kp, size), NULL, 0 ); 60446366Sbostic if ( !rbufp ) return(ERROR); 60546457Sbostic save_bufp = rbufp; 60646366Sbostic 60746457Sbostic /* Pin the bucket chain */ 60846457Sbostic rbufp->flags |= BUF_PIN; 60946366Sbostic for ( bp = (u_short *)rbufp->page, n = *bp++, ndx = 1; ndx < n; ) { 61046366Sbostic if ( bp[1] >= REAL_KEY ) { 61146366Sbostic /* Real key/data pair */ 61246366Sbostic if (size == off - *bp && 61346366Sbostic bcmp(kp, rbufp->page + *bp, size) == 0) { 61446366Sbostic goto found; 61546366Sbostic } 61646366Sbostic off = bp[1]; 61746366Sbostic #ifdef HASH_STATISTICS 61846366Sbostic hash_collisions++; 61946366Sbostic #endif 62046366Sbostic bp += 2; 62146366Sbostic ndx += 2; 62246366Sbostic } else if ( bp[1] == OVFLPAGE ) { 62346366Sbostic rbufp = __get_buf ( *bp, rbufp, 0 ); 62446457Sbostic if ( !rbufp ) { 62546457Sbostic save_bufp->flags &= ~BUF_PIN; 62646457Sbostic return (ERROR); 62746457Sbostic } 62846366Sbostic /* FOR LOOP INIT */ 62946366Sbostic bp = (u_short *)rbufp->page; 63046366Sbostic n = *bp++; 63146366Sbostic ndx = 1; 63246366Sbostic off = hashp->BSIZE; 63346366Sbostic } else if ( bp[1] < REAL_KEY ) { 63446366Sbostic if ( (ndx = __find_bigpair(rbufp, ndx, kp, size )) > 0 ) { 63546366Sbostic goto found; 63646366Sbostic } else if ( ndx == -2 ) { 63746366Sbostic bufp = rbufp; 63846366Sbostic if ( !(pageno = __find_last_page ( &bufp )) ) { 63946366Sbostic ndx = 0; 64046366Sbostic rbufp = bufp; 64146366Sbostic break; /* FOR */ 64246366Sbostic } 64346366Sbostic rbufp = __get_buf ( pageno, bufp, 0 ); 64446457Sbostic if ( !rbufp ) { 64546457Sbostic save_bufp->flags &= ~BUF_PIN; 64646457Sbostic return (ERROR); 64746457Sbostic } 64846366Sbostic /* FOR LOOP INIT */ 64946366Sbostic bp = (u_short *)rbufp->page; 65046366Sbostic n = *bp++; 65146366Sbostic ndx = 1; 65246366Sbostic off = hashp->BSIZE; 65346366Sbostic } else { 65446457Sbostic save_bufp->flags &= ~BUF_PIN; 65546366Sbostic return(ERROR); 65646366Sbostic } 65746366Sbostic } 65846366Sbostic } 65946366Sbostic 66046366Sbostic /* Not found */ 66146366Sbostic switch ( action ) { 66246366Sbostic case HASH_PUT: 66346366Sbostic case HASH_PUTNEW: 66446366Sbostic if (__addel(rbufp, key, val)) { 66546457Sbostic save_bufp->flags &= ~BUF_PIN; 66646366Sbostic return(ERROR); 66746366Sbostic } else { 66846457Sbostic save_bufp->flags &= ~BUF_PIN; 66946366Sbostic return(SUCCESS); 67046366Sbostic } 67146366Sbostic case HASH_GET: 67246366Sbostic case HASH_DELETE: 67346366Sbostic default: 67446457Sbostic save_bufp->flags &= ~BUF_PIN; 67546366Sbostic return(ABNORMAL); 67646366Sbostic } 67746366Sbostic 67846366Sbostic found: 67946366Sbostic switch (action) { 68046366Sbostic case HASH_PUTNEW: 68146457Sbostic save_bufp->flags &= ~BUF_PIN; 68246366Sbostic return(ABNORMAL); 68346366Sbostic case HASH_GET: 68446366Sbostic bp = (u_short *)rbufp->page; 68546366Sbostic if (bp[ndx+1] < REAL_KEY) __big_return(rbufp, ndx, val, 0); 68646366Sbostic else { 68746950Sbostic val->data = (u_char *)rbufp->page + (int) bp[ndx + 1]; 68846366Sbostic val->size = bp[ndx] - bp[ndx + 1]; 68946366Sbostic } 69046366Sbostic break; 69146366Sbostic case HASH_PUT: 69246457Sbostic if ((__delpair (rbufp, ndx)) || (__addel (rbufp, key, val)) ) { 69346457Sbostic save_bufp->flags &= ~BUF_PIN; 69446457Sbostic return(ERROR); 69546457Sbostic } 69646366Sbostic break; 69746366Sbostic case HASH_DELETE: 69846366Sbostic if (__delpair (rbufp, ndx))return(ERROR); 69946366Sbostic break; 70046366Sbostic } 70146457Sbostic save_bufp->flags &= ~BUF_PIN; 70246366Sbostic return (SUCCESS); 70346366Sbostic } 70446366Sbostic 70546366Sbostic static int 70646366Sbostic hash_seq(dbp, key, data, flag) 70746366Sbostic DB *dbp; 70846366Sbostic DBT *key, *data; 709*47251Sbostic u_int flag; 71046366Sbostic { 711*47251Sbostic register u_int bucket; 71246366Sbostic register BUFHEAD *bufp; 71346457Sbostic BUFHEAD *save_bufp; 71446366Sbostic u_short *bp; 71546366Sbostic u_short ndx; 71646366Sbostic u_short pageno; 71746366Sbostic 71846366Sbostic if ( !dbp ) { 71946366Sbostic return (ERROR); 72046366Sbostic } 72146366Sbostic 72246366Sbostic hashp = (HTAB *)dbp->internal; 72346366Sbostic if ( hashp->flags & O_WRONLY ) { 72446366Sbostic errno = EBADF; 72546366Sbostic hashp->errno = errno; 72646366Sbostic return ( ERROR ); 72746366Sbostic } 72846366Sbostic #ifdef HASH_STATISTICS 72946366Sbostic hash_accesses++; 73046366Sbostic #endif 73146366Sbostic 73246366Sbostic if ( (hashp->cbucket < 0) || (flag == R_FIRST) ) { 73346366Sbostic hashp->cbucket = 0; 73446366Sbostic hashp->cndx = 1; 73546366Sbostic hashp->cpage = NULL; 73646366Sbostic } 73746366Sbostic 73846366Sbostic if ( !(bufp = hashp->cpage) ) { 73946366Sbostic for (bucket = hashp->cbucket; 74046366Sbostic bucket <= hashp->MAX_BUCKET; 74146366Sbostic bucket++, hashp->cndx = 1 ) { 74246366Sbostic 74346366Sbostic bufp = __get_buf ( bucket, NULL, 0 ); 74446366Sbostic if (!bufp) return(ERROR); 74546366Sbostic hashp->cpage = bufp; 74646366Sbostic bp = (u_short *)bufp->page; 74746366Sbostic if (bp[0]) break; 74846366Sbostic } 74946366Sbostic hashp->cbucket = bucket; 75046366Sbostic if ( hashp->cbucket > hashp->MAX_BUCKET ) { 75146366Sbostic hashp->cbucket = -1; 75246366Sbostic return ( ABNORMAL ); 75346366Sbostic } 75446366Sbostic } else { 75546366Sbostic bp = (u_short *)hashp->cpage->page; 75646366Sbostic } 75746366Sbostic 75846366Sbostic #ifdef DEBUG 75946366Sbostic assert (bp); 76046366Sbostic assert (bufp); 76146366Sbostic #endif 76246366Sbostic while ( bp[hashp->cndx+1] == OVFLPAGE ) { 76346366Sbostic bufp = hashp->cpage = __get_buf ( bp[hashp->cndx], bufp, 0 ); 76446366Sbostic if (!bufp) return(ERROR); 76546366Sbostic bp = (u_short *)(bufp->page); 76646366Sbostic hashp->cndx = 1; 76746366Sbostic } 76846366Sbostic ndx = hashp->cndx; 76946366Sbostic if (bp[ndx+1] < REAL_KEY) { 77046366Sbostic if (__big_keydata(bufp, ndx, key, data, 1)) { 77146366Sbostic return(ERROR); 77246366Sbostic } 77346366Sbostic } else { 77446950Sbostic key->data = (u_char *)hashp->cpage->page + bp[ndx]; 77546366Sbostic key->size = (ndx > 1 ? bp[ndx-1] : hashp->BSIZE) - bp[ndx]; 77646950Sbostic data->data = (u_char *)hashp->cpage->page + bp[ndx + 1]; 77746366Sbostic data->size = bp[ndx] - bp[ndx + 1]; 77846366Sbostic ndx += 2; 77946366Sbostic if ( ndx > bp[0] ) { 78046366Sbostic hashp->cpage = NULL; 78146366Sbostic hashp->cbucket++; 78246366Sbostic hashp->cndx=1; 78346366Sbostic } else hashp->cndx = ndx; 78446366Sbostic } 78546366Sbostic return (SUCCESS); 78646366Sbostic } 78746366Sbostic 78846366Sbostic /********************************* UTILITIES ************************/ 78946366Sbostic /* 79046366Sbostic 0 ==> OK 79146366Sbostic -1 ==> Error 79246366Sbostic */ 79346366Sbostic extern int 79446366Sbostic __expand_table() 79546366Sbostic { 796*47251Sbostic u_int old_bucket, new_bucket; 79746366Sbostic int new_segnum; 79846366Sbostic int dirsize; 79946366Sbostic int spare_ndx; 80046366Sbostic register char **old, **new; 80146366Sbostic #ifdef HASH_STATISTICS 80246366Sbostic hash_expansions++; 80346366Sbostic #endif 80446366Sbostic 80546366Sbostic new_bucket = ++hashp->MAX_BUCKET; 80646366Sbostic old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK); 80746366Sbostic 80846366Sbostic new_segnum = new_bucket >> hashp->SSHIFT; 80946366Sbostic 81046366Sbostic /* Check if we need a new segment */ 81146366Sbostic if ( new_segnum >= hashp->nsegs ) { 81246366Sbostic 81346366Sbostic /* Check if we need to expand directory */ 81446366Sbostic if ( new_segnum >= hashp->DSIZE ) { 81546366Sbostic 81646366Sbostic /* Reallocate directory */ 81746366Sbostic dirsize = hashp->DSIZE * sizeof ( SEGMENT * ); 81846366Sbostic if (!hash_realloc ( &hashp->dir, dirsize, (dirsize << 1 ) )) { 81946366Sbostic return(-1); 82046366Sbostic } 82146366Sbostic hashp->DSIZE = dirsize << 1; 82246366Sbostic } 82346366Sbostic if (!(hashp->dir[new_segnum] = 82446366Sbostic (SEGMENT)calloc ( hashp->SGSIZE, sizeof(SEGMENT)))) { 82546366Sbostic return(-1); 82646366Sbostic } 82746366Sbostic hashp->exsegs++; 82846366Sbostic hashp->nsegs++; 82946366Sbostic } 83046366Sbostic 83146366Sbostic /* 83246366Sbostic If the split point is increasing (MAX_BUCKET's log 83346366Sbostic base 2 increases), we need to copy the current contents 83446366Sbostic of the spare split bucket to the next bucket 83546366Sbostic */ 83646366Sbostic spare_ndx = __log2(hashp->MAX_BUCKET); 83746366Sbostic if ( spare_ndx != (__log2(hashp->MAX_BUCKET - 1))) { 83846366Sbostic hashp->SPARES[spare_ndx] = hashp->SPARES[spare_ndx-1]; 83946366Sbostic } 84046366Sbostic 84146366Sbostic if ( new_bucket > hashp->HIGH_MASK ) { 84246366Sbostic /* Starting a new doubling */ 84346366Sbostic hashp->LOW_MASK = hashp->HIGH_MASK; 84446366Sbostic hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK; 84546366Sbostic } 84646366Sbostic 84746366Sbostic /* 84846366Sbostic * Relocate records to the new bucket 84946366Sbostic */ 85046366Sbostic return(__split_page ( old_bucket, new_bucket )); 85146366Sbostic } 85246366Sbostic /* 85346366Sbostic If realloc guarantees that the pointer is not destroyed 85446366Sbostic if the realloc fails, then this routine can go away 85546366Sbostic */ 85646366Sbostic static char * 85746366Sbostic hash_realloc ( p_ptr, oldsize, newsize ) 85846366Sbostic char **p_ptr; 85946366Sbostic int oldsize; 86046366Sbostic int newsize; 86146366Sbostic { 86246366Sbostic register char *p; 86346366Sbostic 86446366Sbostic if (p = (char *)malloc ( newsize ) ) { 86546366Sbostic bcopy ( *p_ptr, p, oldsize ); 86646366Sbostic bzero ( *p_ptr + oldsize, newsize-oldsize ); 86746366Sbostic free(*p_ptr); 86846366Sbostic *p_ptr = p; 86946366Sbostic } 87046366Sbostic return (p); 87146366Sbostic } 87246366Sbostic 873*47251Sbostic extern u_int 87446366Sbostic __call_hash ( k, len ) 87546366Sbostic char *k; 87646366Sbostic int len; 87746366Sbostic { 87846366Sbostic int n, bucket; 87946366Sbostic n = hashp->hash(k, len); 88046366Sbostic 88146366Sbostic bucket = n & hashp->HIGH_MASK; 88246366Sbostic if ( bucket > hashp->MAX_BUCKET ) { 88346366Sbostic bucket = bucket & hashp->LOW_MASK; 88446366Sbostic } 88546366Sbostic 88646366Sbostic return(bucket); 88746366Sbostic } 88846366Sbostic 88946366Sbostic /* 89046366Sbostic Allocate segment table. On error, destroy the table 89146366Sbostic and set errno. 89246366Sbostic 89346366Sbostic Returns 0 on success 89446366Sbostic */ 89546366Sbostic static int 89646366Sbostic alloc_segs ( nsegs ) 89746366Sbostic int nsegs; 89846366Sbostic { 89946366Sbostic register int i; 90046366Sbostic register SEGMENT store; 90146366Sbostic 90246366Sbostic int save_errno; 90346366Sbostic 90446366Sbostic if (!(hashp->dir = (SEGMENT *)calloc(hashp->DSIZE, sizeof(SEGMENT *)))) { 90546366Sbostic save_errno = errno; 90646366Sbostic (void)hdestroy(); 90746366Sbostic errno = save_errno; 90846366Sbostic return(-1); 90946366Sbostic } 91046366Sbostic 91146366Sbostic /* Allocate segments */ 91246366Sbostic store = (SEGMENT)calloc ( nsegs << hashp->SSHIFT, sizeof (SEGMENT) ); 91346366Sbostic if ( !store ) { 91446366Sbostic save_errno = errno; 91546366Sbostic (void)hdestroy(); 91646366Sbostic errno = save_errno; 91746366Sbostic return(-1); 91846366Sbostic } 91946366Sbostic 92046366Sbostic for ( i=0; i < nsegs; i++, hashp->nsegs++ ) { 92146366Sbostic hashp->dir[i] = &store[i<<hashp->SSHIFT]; 92246366Sbostic } 92346366Sbostic return(0); 92446366Sbostic } 92546366Sbostic 92646366Sbostic /* 92746366Sbostic Hashp->hdr needs to be byteswapped. 92846366Sbostic */ 92946366Sbostic static void 93046366Sbostic swap_header_copy ( srcp, destp ) 93146366Sbostic HASHHDR *srcp; 93246366Sbostic HASHHDR *destp; 93346366Sbostic { 93446366Sbostic int i; 93546366Sbostic 93646366Sbostic BLSWAP_COPY(srcp->magic, destp->magic); 93746366Sbostic BLSWAP_COPY(srcp->version, destp->version); 93846366Sbostic BLSWAP_COPY(srcp->lorder, destp->lorder); 93946366Sbostic BLSWAP_COPY(srcp->bsize, destp->bsize); 94046366Sbostic BLSWAP_COPY(srcp->bshift, destp->bshift); 94146366Sbostic BLSWAP_COPY(srcp->dsize, destp->dsize); 94246366Sbostic BLSWAP_COPY(srcp->ssize, destp->ssize); 94346366Sbostic BLSWAP_COPY(srcp->sshift, destp->sshift); 94446366Sbostic BLSWAP_COPY(srcp->max_bucket, destp->max_bucket); 94546366Sbostic BLSWAP_COPY(srcp->high_mask, destp->high_mask); 94646366Sbostic BLSWAP_COPY(srcp->low_mask, destp->low_mask); 94746366Sbostic BLSWAP_COPY(srcp->ffactor, destp->ffactor); 94846366Sbostic BLSWAP_COPY(srcp->nkeys, destp->nkeys); 94946366Sbostic BLSWAP_COPY(srcp->hdrpages, destp->hdrpages); 95046366Sbostic BLSWAP_COPY(srcp->h_charkey, destp->h_charkey); 95146366Sbostic for ( i=0; i < NCACHED; i++ ) { 95246366Sbostic BLSWAP_COPY ( srcp->spares[i] , destp->spares[i]); 95346366Sbostic BSSWAP_COPY ( srcp->bitmaps[i] , destp->bitmaps[i]); 95446366Sbostic } 95546366Sbostic return; 95646366Sbostic } 95746366Sbostic 95846366Sbostic static void 95946366Sbostic swap_header ( ) 96046366Sbostic { 96146366Sbostic HASHHDR *hdrp; 96246366Sbostic int i; 96346366Sbostic 96446366Sbostic hdrp = &hashp->hdr; 96546366Sbostic 96646366Sbostic BLSWAP(hdrp->magic); 96746366Sbostic BLSWAP(hdrp->version); 96846366Sbostic BLSWAP(hdrp->lorder); 96946366Sbostic BLSWAP(hdrp->bsize); 97046366Sbostic BLSWAP(hdrp->bshift); 97146366Sbostic BLSWAP(hdrp->dsize); 97246366Sbostic BLSWAP(hdrp->ssize); 97346366Sbostic BLSWAP(hdrp->sshift); 97446366Sbostic BLSWAP(hdrp->max_bucket); 97546366Sbostic BLSWAP(hdrp->high_mask); 97646366Sbostic BLSWAP(hdrp->low_mask); 97746366Sbostic BLSWAP(hdrp->ffactor); 97846366Sbostic BLSWAP(hdrp->nkeys); 97946366Sbostic BLSWAP(hdrp->hdrpages); 98046366Sbostic BLSWAP(hdrp->h_charkey); 98146366Sbostic for ( i=0; i < NCACHED; i++ ) { 98246366Sbostic BLSWAP ( hdrp->spares[i] ); 98346366Sbostic BSSWAP ( hdrp->bitmaps[i] ); 98446366Sbostic } 98546366Sbostic return; 98646366Sbostic } 987