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*46500Sbostic static char sccsid[] = "@(#)hash.c 5.6 (Berkeley) 02/21/91"; 1346366Sbostic #endif /* LIBC_SCCS and not lint */ 1446366Sbostic 1546366Sbostic #include <sys/param.h> 1646366Sbostic #include <sys/file.h> 1746366Sbostic #include <sys/stat.h> 1846366Sbostic #include <errno.h> 1946366Sbostic #include <assert.h> 2046366Sbostic #include <db.h> 21*46500Sbostic #include <unistd.h> 2246366Sbostic #include "hash.h" 2346366Sbostic #include <string.h> 2446366Sbostic 2546366Sbostic /* Fast arithmetic, relying on powers of 2, */ 2646366Sbostic 2746366Sbostic #define MOD(x,y) ((x) & ((y)-1)) 2846366Sbostic #define RETURN_ERROR(ERR,LOC) { save_errno = ERR; goto LOC; } 2946366Sbostic 3046366Sbostic /* Return values */ 3146366Sbostic 3246366Sbostic #define SUCCESS 0 3346366Sbostic #define ERROR -1 3446366Sbostic #define ABNORMAL 1 3546366Sbostic 3646366Sbostic /* external routines */ 3746366Sbostic extern char *calloc(); 3846366Sbostic 3946366Sbostic /* page.c */ 4046366Sbostic extern int __get_page(); 4146366Sbostic extern int __split_page(); 4246366Sbostic extern int __addel(); 4346366Sbostic extern int __delpair(); 4446366Sbostic extern u_long *__init_bitmap(); 4546366Sbostic 4646366Sbostic /* big.c */ 4746366Sbostic extern void __big_return(); 4846366Sbostic extern int __big_keydata(); 4946366Sbostic extern u_short __find_last_page(); 5046366Sbostic 5146366Sbostic /* buf.c */ 5246366Sbostic extern BUFHEAD *__get_buf(); 5346366Sbostic extern void __buf_init(); 5446366Sbostic extern int __buf_free(); 5546366Sbostic 5646366Sbostic /* hash function */ 5746366Sbostic extern int (*default_hash)(); 5846366Sbostic 5946366Sbostic /* My externals */ 6046366Sbostic extern int __expand_table(); 6146366Sbostic extern int __call_hash(); 6246366Sbostic 6346366Sbostic /* Internal routines */ 6446366Sbostic static HTAB *init_hash(); 6546366Sbostic static int hash_access(); 6646366Sbostic static int flush_meta(); 6746366Sbostic static int alloc_segs(); 6846366Sbostic static int init_htab(); 6946366Sbostic static char *hash_realloc(); 7046366Sbostic static int hdestroy(); 7146366Sbostic static int hash_put(); 7246366Sbostic static int hash_delete(); 7346366Sbostic static int hash_get(); 7446366Sbostic static int hash_sync(); 7546366Sbostic static int hash_close(); 7646366Sbostic static int hash_seq(); 7746366Sbostic static void swap_header_copy(); 7846366Sbostic static void swap_header(); 7946366Sbostic 8046366Sbostic /* Local data */ 8146366Sbostic 8246366Sbostic HTAB *hashp = NULL; 8346366Sbostic 8446366Sbostic #ifdef HASH_STATISTICS 8546366Sbostic long hash_accesses, hash_collisions, hash_expansions, hash_overflows; 8646366Sbostic #endif 8746366Sbostic 8846366Sbostic /************************** INTERFACE ROUTINES **********************/ 8946366Sbostic /* OPEN/CLOSE */ 9046366Sbostic 9146366Sbostic extern DB * 9246366Sbostic hash_open ( file, flags, mode, info ) 9346366Sbostic char *file; 9446366Sbostic int flags; 9546366Sbostic int mode; 9646366Sbostic HASHINFO *info; /* Special directives for create */ 9746366Sbostic { 9846366Sbostic int buckets; 9946366Sbostic int bpages; 10046366Sbostic int hdrsize; 10146366Sbostic int i; 10246366Sbostic int new_table = 0; 10346366Sbostic int nkey; 10446366Sbostic int nsegs; 10546366Sbostic int save_errno; 10646366Sbostic struct stat statbuf; 10746366Sbostic DB *dbp; 10846366Sbostic 10946366Sbostic 11046366Sbostic if ( !(hashp = (HTAB *) calloc( 1, sizeof(HTAB) ))) { 11146366Sbostic /* calloc should set errno */ 11246366Sbostic return(0); 11346366Sbostic } 11446457Sbostic hashp->fp = -1; 11546366Sbostic /* 11646366Sbostic Select flags relevant to us. 11746366Sbostic Even if user wants write only, we need to be able to read 11846366Sbostic the actual file, so we need to open it read/write. But, the 11946366Sbostic field in the hashp structure needs to be accurate so that 12046366Sbostic we can check accesses. 12146366Sbostic */ 12246366Sbostic flags = flags & (O_CREAT | O_EXCL | O_RDONLY | O_RDWR | O_TRUNC | O_WRONLY); 12346366Sbostic hashp->flags = flags; 12446366Sbostic if ( flags & O_WRONLY ) flags = (flags & ~O_WRONLY) | O_RDWR; 12546366Sbostic 12646366Sbostic if ( !file || 12746366Sbostic (flags & O_TRUNC) || 12846366Sbostic (stat ( file, &statbuf ) && (errno == ENOENT)) ) { 12946366Sbostic new_table = 1; 13046366Sbostic } 13146366Sbostic 13246485Sbostic if ( file ) { 13346485Sbostic if ((hashp->fp = open ( file, flags, mode )) == -1) { 13446485Sbostic RETURN_ERROR (errno, error0); 13546485Sbostic } 13646485Sbostic (void)fcntl(hashp->fp, F_SETFD, 1); 13746366Sbostic } 13846366Sbostic 13946366Sbostic if ( new_table ) { 14046366Sbostic if ( !(hashp = init_hash( info )) ) { 14146366Sbostic RETURN_ERROR(errno,error1); 14246366Sbostic } 14346366Sbostic } else { 14446366Sbostic /* Table already exists */ 14546366Sbostic if ( info && info->hash ) hashp->hash = info->hash; 14646366Sbostic else hashp->hash = default_hash; 14746366Sbostic 14846366Sbostic hdrsize = read ( hashp->fp, &hashp->hdr, sizeof(HASHHDR) ); 14946366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN 15046366Sbostic swap_header ( ); 15146366Sbostic #endif 15246366Sbostic if ( hdrsize == -1 ) { 15346366Sbostic RETURN_ERROR(errno, error1); 15446366Sbostic } 15546366Sbostic if ( hdrsize != sizeof(HASHHDR) ) { 15646366Sbostic RETURN_ERROR(EFTYPE, error1); 15746366Sbostic } 15846366Sbostic 15946366Sbostic /* Verify file type, versions and hash function */ 16046366Sbostic if ( hashp->MAGIC != HASHMAGIC ) 16146366Sbostic RETURN_ERROR ( EFTYPE, error1 ); 16246366Sbostic if ( hashp->VERSION != VERSION_NO ) 16346366Sbostic RETURN_ERROR ( EFTYPE, error1 ); 16446366Sbostic if (hashp->hash ( CHARKEY, sizeof(CHARKEY) ) != hashp->H_CHARKEY ) { 16546366Sbostic RETURN_ERROR ( EFTYPE, error1 ); 16646366Sbostic } 16746366Sbostic 16846366Sbostic /* 16946366Sbostic Figure out how many segments we need. 17046366Sbostic */ 17146366Sbostic nsegs = (hashp->MAX_BUCKET + hashp->SGSIZE -1)/ hashp->SGSIZE; 17246366Sbostic hashp->nsegs = 0; 17346366Sbostic if (alloc_segs( nsegs )) { 17446366Sbostic /* 17546366Sbostic If alloc_segs fails, table will have been destroyed 17646366Sbostic and errno will have been set. 17746366Sbostic */ 17846366Sbostic return( (DB *) NULL ); 17946366Sbostic } 18046366Sbostic 18146366Sbostic /* Read in bitmaps */ 18246366Sbostic bpages = (hashp->SPARES[__log2(hashp->MAX_BUCKET)] + 18346366Sbostic (hashp->BSIZE << BYTE_SHIFT) - 1) >> 18446366Sbostic (hashp->BSHIFT + BYTE_SHIFT); 18546366Sbostic 18646366Sbostic hashp->mapp[0] = (u_long *)malloc(bpages<<hashp->BSHIFT); 18746366Sbostic if ( !hashp->mapp[0] ) { 18846366Sbostic RETURN_ERROR(errno, error2); 18946366Sbostic } 19046366Sbostic for ( i = 0; i < bpages; i++ ) { 19146366Sbostic hashp->mapp[i] = &hashp->mapp[0][i<<hashp->BSHIFT]; 19246366Sbostic if (__get_page ((char *)hashp->mapp[i], 19346366Sbostic hashp->BITMAPS[i], 0, 1, 1)) { 19446366Sbostic RETURN_ERROR(errno, error2); 19546366Sbostic } 19646366Sbostic } 19746366Sbostic 19846366Sbostic } 19946366Sbostic 20046366Sbostic /* Initialize Buffer Manager */ 20146366Sbostic if ( info && info->ncached ) { 20246366Sbostic __buf_init (info->ncached); 20346366Sbostic } else { 20446366Sbostic __buf_init (DEF_BUFSIZE); 20546366Sbostic } 20646366Sbostic 20746366Sbostic hashp->new_file = new_table; 20846457Sbostic hashp->save_file = file && !(hashp->flags&O_RDONLY); 20946366Sbostic hashp->cbucket = -1; 21046366Sbostic if ( !(dbp = (DB *)malloc ( sizeof (DB) )) ) { 21146366Sbostic save_errno = errno; 21246366Sbostic hdestroy(); 21346366Sbostic errno = save_errno; 21446366Sbostic return ( NULL ); 21546366Sbostic } 21646366Sbostic dbp->internal = (char *)hashp; 21746366Sbostic dbp->close = hash_close; 21846366Sbostic dbp->delete = hash_delete; 21946366Sbostic dbp->get = hash_get; 22046366Sbostic dbp->put = hash_put; 22146366Sbostic dbp->seq = hash_seq; 22246366Sbostic dbp->sync = hash_sync; 22346366Sbostic 22446366Sbostic #ifdef DEBUG 22546366Sbostic (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", 22646366Sbostic "init_htab:", 22746366Sbostic "TABLE POINTER ", hashp, 22846366Sbostic "BUCKET SIZE ", hashp->BSIZE, 22946366Sbostic "BUCKET SHIFT ", hashp->BSHIFT, 23046366Sbostic "DIRECTORY SIZE ", hashp->DSIZE, 23146366Sbostic "SEGMENT SIZE ", hashp->SGSIZE, 23246366Sbostic "SEGMENT SHIFT ", hashp->SSHIFT, 23346366Sbostic "FILL FACTOR ", hashp->FFACTOR, 23446366Sbostic "MAX BUCKET ", hashp->MAX_BUCKET, 23546366Sbostic "HIGH MASK ", hashp->HIGH_MASK, 23646366Sbostic "LOW MASK ", hashp->LOW_MASK, 23746366Sbostic "NSEGS ", hashp->nsegs, 23846366Sbostic "NKEYS ", hashp->NKEYS ); 23946366Sbostic #endif 24046366Sbostic #ifdef HASH_STATISTICS 24146366Sbostic hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0; 24246366Sbostic #endif 24346366Sbostic return (dbp); 24446366Sbostic 24546366Sbostic error2: 24646366Sbostic (void)hdestroy(); 24746366Sbostic errno = save_errno; 24846366Sbostic return ( (DB *)NULL ); 24946366Sbostic 25046366Sbostic error1: 25146366Sbostic (void) close ( hashp->fp ); 25246366Sbostic 25346366Sbostic error0: 25446366Sbostic free ( hashp ); 25546366Sbostic errno = save_errno; 25646366Sbostic return ( (DB *) NULL ); 25746366Sbostic } 25846366Sbostic 25946366Sbostic static int 26046366Sbostic hash_close (dbp) 26146366Sbostic DB *dbp; 26246366Sbostic { 26346457Sbostic int retval; 26446457Sbostic 26546366Sbostic if ( !dbp ) { 26646366Sbostic return(ERROR); 26746366Sbostic } 26846366Sbostic hashp = (HTAB *)dbp->internal; 26946457Sbostic retval = hdestroy(); 27046457Sbostic (void)free ( dbp ); 27146457Sbostic return ( retval ); 27246366Sbostic } 27346366Sbostic 27446366Sbostic 27546366Sbostic /************************** LOCAL CREATION ROUTINES **********************/ 27646366Sbostic static HTAB * 27746366Sbostic init_hash( info ) 27846366Sbostic HASHINFO *info; 27946366Sbostic { 28046366Sbostic int nelem; 28146366Sbostic 28246366Sbostic nelem = 1; 28346366Sbostic 28446366Sbostic hashp->LORDER = BYTE_ORDER; 28546366Sbostic hashp->BSIZE = DEF_BUCKET_SIZE; 28646366Sbostic hashp->BSHIFT = DEF_BUCKET_SHIFT; 28746366Sbostic hashp->SGSIZE = DEF_SEGSIZE; 28846366Sbostic hashp->SSHIFT = DEF_SEGSIZE_SHIFT; 28946366Sbostic hashp->DSIZE = DEF_DIRSIZE; 29046366Sbostic hashp->FFACTOR = DEF_FFACTOR; 29146366Sbostic hashp->hash = default_hash; 29246366Sbostic bzero (hashp->SPARES, sizeof (hashp->SPARES)); 29346366Sbostic 29446366Sbostic if ( info ) { 29546366Sbostic if ( info->bsize ) { 29646366Sbostic /* Round pagesize up to power of 2 */ 29746366Sbostic hashp->BSHIFT = __log2(info->bsize); 29846366Sbostic hashp->BSIZE = 1 << hashp->BSHIFT; 29946366Sbostic if ( hashp->BSIZE > MAX_BSIZE ) { 30046366Sbostic errno = EINVAL; 30146366Sbostic return(0); 30246366Sbostic } 30346366Sbostic } 30446366Sbostic if ( info->ffactor ) hashp->FFACTOR = info->ffactor; 30546366Sbostic if ( info->hash ) hashp->hash = info->hash; 30646366Sbostic if ( info->nelem ) nelem = info->nelem; 30746366Sbostic if ( info->lorder ) { 30846366Sbostic if ( info->lorder != BIG_ENDIAN && 30946366Sbostic info->lorder != LITTLE_ENDIAN) { 31046366Sbostic errno = EINVAL; 31146366Sbostic return(0); 31246366Sbostic } 31346366Sbostic hashp->LORDER = info->lorder; 31446366Sbostic } 31546366Sbostic } 31646366Sbostic 31746366Sbostic /* init_htab should destroy the table and set errno if it fails */ 31846366Sbostic if ( init_htab ( nelem ) ) { 31946366Sbostic return(0); 32046366Sbostic } else { 32146366Sbostic return(hashp); 32246366Sbostic } 32346366Sbostic } 32446366Sbostic 32546366Sbostic /* 32646366Sbostic This calls alloc_segs which may run out of memory. 32746366Sbostic Alloc_segs will destroy the table and set errno, 32846366Sbostic so we just pass the error information along. 32946366Sbostic 33046366Sbostic Returns 0 on No Error 33146366Sbostic 33246366Sbostic */ 33346366Sbostic static int 33446366Sbostic init_htab ( nelem ) 33546366Sbostic int nelem; 33646366Sbostic { 33746366Sbostic register SEGMENT *segp; 33846366Sbostic register int nbuckets; 33946366Sbostic register int nsegs; 34046366Sbostic int l2; 34146366Sbostic 34246366Sbostic /* 34346366Sbostic * Divide number of elements by the fill factor and determine a desired 34446366Sbostic * number of buckets. Allocate space for the next greater power of 34546366Sbostic * two number of buckets 34646366Sbostic */ 34746366Sbostic nelem = (nelem - 1) / hashp->FFACTOR + 1; 34846366Sbostic 34946366Sbostic l2 = __log2(nelem); 35046366Sbostic nbuckets = 1 << l2; 35146366Sbostic nbuckets = MAX ( nbuckets, 2 ); 35246366Sbostic 35346366Sbostic hashp->SPARES[l2] = l2 + 1; 35446366Sbostic hashp->SPARES[l2+1] = l2 + 1; 35546366Sbostic /* 35646366Sbostic First bitmap page is at: 35746366Sbostic splitpoint l2 35846366Sbostic page offset 1 35946366Sbostic */ 36046366Sbostic __init_bitmap ( OADDR_OF(l2,1), l2+1, 0 ); 36146366Sbostic 36246366Sbostic hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1; 36346366Sbostic hashp->HIGH_MASK = (nbuckets << 1) - 1; 36446366Sbostic hashp->HDRPAGES = ((MAX(sizeof(HASHHDR),MINHDRSIZE) - 1) >> 36546366Sbostic hashp->BSHIFT) + 1; 36646366Sbostic 36746366Sbostic nsegs = (nbuckets - 1) / hashp->SGSIZE + 1; 36846366Sbostic nsegs = 1 << __log2(nsegs); 36946366Sbostic 37046366Sbostic if ( nsegs > hashp->DSIZE ) { 37146366Sbostic hashp->DSIZE = nsegs; 37246366Sbostic } 37346366Sbostic 37446366Sbostic return (alloc_segs ( nsegs ) ); 37546366Sbostic } 37646366Sbostic 37746366Sbostic /********************** DESTROY/CLOSE ROUTINES ************************/ 37846366Sbostic 37946366Sbostic /* 38046366Sbostic Flushes any changes to the file if necessary and 38146366Sbostic destroys the hashp structure, freeing all allocated 38246366Sbostic space. 38346366Sbostic */ 38446366Sbostic static int 38546366Sbostic hdestroy() 38646366Sbostic { 38746366Sbostic int save_errno; 38846366Sbostic int i; 38946366Sbostic 39046366Sbostic save_errno = 0; 39146366Sbostic 39246366Sbostic if (hashp != NULL) { 39346366Sbostic #ifdef HASH_STATISTICS 39446366Sbostic (void) fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n", 39546366Sbostic hash_accesses, hash_collisions); 39646366Sbostic (void) fprintf(stderr, "hdestroy: expansions %ld\n", 39746366Sbostic hash_expansions); 39846366Sbostic (void) fprintf(stderr, "hdestroy: overflows %ld\n", 39946366Sbostic hash_overflows); 40046366Sbostic (void) fprintf(stderr, "keys %ld maxp %d segmentcount %d\n", 40146366Sbostic hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs); 40246366Sbostic 40346366Sbostic for ( i = 0; i < NCACHED; i++ ) { 40446366Sbostic (void) fprintf ( stderr, "spares[%d] = %d\n", i, hashp->SPARES[i] ); 40546366Sbostic } 40646366Sbostic #endif 40746366Sbostic 40846366Sbostic /* 40946366Sbostic Call on buffer manager to free buffers, and if 41046366Sbostic required, write them to disk 41146366Sbostic */ 41246366Sbostic if (__buf_free(1, hashp->save_file)) { 41346366Sbostic save_errno = errno; 41446366Sbostic } 41546366Sbostic if ( hashp->dir ) { 41646366Sbostic (void)free(*hashp->dir); /* Free initial segments */ 41746366Sbostic /* Free extra segments */ 41846366Sbostic while ( hashp->exsegs-- ) { 41946366Sbostic (void)free ( hashp->dir[--hashp->nsegs] ); 42046366Sbostic } 42146366Sbostic (void)free(hashp->dir); 42246366Sbostic } 42346366Sbostic if (flush_meta() && !save_errno) { 42446366Sbostic save_errno = errno; 42546366Sbostic } 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 488*46500Sbostic 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 51446366Sbostic hash_get ( dbp, key, data ) 51546366Sbostic DB *dbp; 51646366Sbostic DBT *key, *data; 51746366Sbostic { 51846366Sbostic if ( !dbp ) { 51946366Sbostic return (ERROR); 52046366Sbostic } 52146366Sbostic hashp = (HTAB *)dbp->internal; 52246366Sbostic if ( hashp->flags & O_WRONLY ) { 52346366Sbostic errno = EBADF; 52446366Sbostic hashp->errno = errno; 52546366Sbostic return ( ERROR ); 52646366Sbostic } 52746366Sbostic return ( hash_access ( HASH_GET, key, data ) ); 52846366Sbostic } 52946366Sbostic 53046366Sbostic static int 53146366Sbostic hash_put ( dbp, key, data, flag ) 53246366Sbostic DB *dbp; 53346366Sbostic DBT *key, *data; 53446366Sbostic int flag; 53546366Sbostic { 53646366Sbostic if ( !dbp ) { 53746366Sbostic return (ERROR); 53846366Sbostic } 53946366Sbostic hashp = (HTAB *)dbp->internal; 54046366Sbostic if ( (hashp->flags & O_ACCMODE) == O_RDONLY ) { 54146366Sbostic errno = EBADF; 54246366Sbostic hashp->errno = errno; 54346366Sbostic return ( ERROR ); 54446366Sbostic } 54546366Sbostic if ( flag == R_NOOVERWRITE ) { 54646366Sbostic return ( hash_access ( HASH_PUTNEW, key, data ) ); 54746366Sbostic } else { 54846366Sbostic return ( hash_access ( HASH_PUT, key, data ) ); 54946366Sbostic } 55046366Sbostic } 55146366Sbostic 55246366Sbostic static int 55346366Sbostic hash_delete ( dbp, key, flags ) 55446366Sbostic DB *dbp; 55546366Sbostic DBT *key; 55646366Sbostic int flags; /* Ignored */ 55746366Sbostic { 55846366Sbostic if ( !dbp ) { 55946366Sbostic return (ERROR); 56046366Sbostic } 56146366Sbostic hashp = (HTAB *)dbp->internal; 56246366Sbostic if ( (hashp->flags & O_ACCMODE) == O_RDONLY ) { 56346366Sbostic errno = EBADF; 56446366Sbostic hashp->errno = errno; 56546366Sbostic return ( ERROR ); 56646366Sbostic } 56746366Sbostic return ( hash_access ( HASH_DELETE, key, NULL ) ); 56846366Sbostic } 56946366Sbostic 57046366Sbostic /* 57146366Sbostic Assume that hashp has been set in wrapper routine 57246366Sbostic */ 57346366Sbostic static int 57446366Sbostic hash_access(action, key, val) 57546366Sbostic ACTION action; 57646366Sbostic DBT *key, *val; 57746366Sbostic { 57846366Sbostic register BUFHEAD *rbufp; 57946366Sbostic register u_short *bp; 58046366Sbostic register int ndx; 58146366Sbostic register int n; 58246366Sbostic register int off = hashp->BSIZE; 58346366Sbostic register int size; 58446366Sbostic register char *kp; 58546366Sbostic BUFHEAD *bufp; 58646457Sbostic BUFHEAD *save_bufp; 58746366Sbostic u_short pageno; 58846366Sbostic 58946366Sbostic #ifdef HASH_STATISTICS 59046366Sbostic hash_accesses++; 59146366Sbostic #endif 59246366Sbostic 59346366Sbostic size = key->size; 59446366Sbostic kp = key->data; 59546457Sbostic rbufp = __get_buf ( __call_hash(kp, size), NULL, 0 ); 59646366Sbostic if ( !rbufp ) return(ERROR); 59746457Sbostic save_bufp = rbufp; 59846366Sbostic 59946457Sbostic /* Pin the bucket chain */ 60046457Sbostic rbufp->flags |= BUF_PIN; 60146366Sbostic for ( bp = (u_short *)rbufp->page, n = *bp++, ndx = 1; ndx < n; ) { 60246366Sbostic if ( bp[1] >= REAL_KEY ) { 60346366Sbostic /* Real key/data pair */ 60446366Sbostic if (size == off - *bp && 60546366Sbostic bcmp(kp, rbufp->page + *bp, size) == 0) { 60646366Sbostic goto found; 60746366Sbostic } 60846366Sbostic off = bp[1]; 60946366Sbostic #ifdef HASH_STATISTICS 61046366Sbostic hash_collisions++; 61146366Sbostic #endif 61246366Sbostic bp += 2; 61346366Sbostic ndx += 2; 61446366Sbostic } else if ( bp[1] == OVFLPAGE ) { 61546366Sbostic rbufp = __get_buf ( *bp, rbufp, 0 ); 61646457Sbostic if ( !rbufp ) { 61746457Sbostic save_bufp->flags &= ~BUF_PIN; 61846457Sbostic return (ERROR); 61946457Sbostic } 62046366Sbostic /* FOR LOOP INIT */ 62146366Sbostic bp = (u_short *)rbufp->page; 62246366Sbostic n = *bp++; 62346366Sbostic ndx = 1; 62446366Sbostic off = hashp->BSIZE; 62546366Sbostic } else if ( bp[1] < REAL_KEY ) { 62646366Sbostic if ( (ndx = __find_bigpair(rbufp, ndx, kp, size )) > 0 ) { 62746366Sbostic goto found; 62846366Sbostic } else if ( ndx == -2 ) { 62946366Sbostic bufp = rbufp; 63046366Sbostic if ( !(pageno = __find_last_page ( &bufp )) ) { 63146366Sbostic ndx = 0; 63246366Sbostic rbufp = bufp; 63346366Sbostic break; /* FOR */ 63446366Sbostic } 63546366Sbostic rbufp = __get_buf ( pageno, bufp, 0 ); 63646457Sbostic if ( !rbufp ) { 63746457Sbostic save_bufp->flags &= ~BUF_PIN; 63846457Sbostic return (ERROR); 63946457Sbostic } 64046366Sbostic /* FOR LOOP INIT */ 64146366Sbostic bp = (u_short *)rbufp->page; 64246366Sbostic n = *bp++; 64346366Sbostic ndx = 1; 64446366Sbostic off = hashp->BSIZE; 64546366Sbostic } else { 64646457Sbostic save_bufp->flags &= ~BUF_PIN; 64746366Sbostic return(ERROR); 64846366Sbostic } 64946366Sbostic } 65046366Sbostic } 65146366Sbostic 65246366Sbostic /* Not found */ 65346366Sbostic switch ( action ) { 65446366Sbostic case HASH_PUT: 65546366Sbostic case HASH_PUTNEW: 65646366Sbostic if (__addel(rbufp, key, val)) { 65746457Sbostic save_bufp->flags &= ~BUF_PIN; 65846366Sbostic return(ERROR); 65946366Sbostic } else { 66046457Sbostic save_bufp->flags &= ~BUF_PIN; 66146366Sbostic return(SUCCESS); 66246366Sbostic } 66346366Sbostic case HASH_GET: 66446366Sbostic case HASH_DELETE: 66546366Sbostic default: 66646457Sbostic save_bufp->flags &= ~BUF_PIN; 66746366Sbostic return(ABNORMAL); 66846366Sbostic } 66946366Sbostic 67046366Sbostic found: 67146366Sbostic switch (action) { 67246366Sbostic case HASH_PUTNEW: 67346457Sbostic save_bufp->flags &= ~BUF_PIN; 67446366Sbostic return(ABNORMAL); 67546366Sbostic case HASH_GET: 67646366Sbostic bp = (u_short *)rbufp->page; 67746366Sbostic if (bp[ndx+1] < REAL_KEY) __big_return(rbufp, ndx, val, 0); 67846366Sbostic else { 67946366Sbostic val->data = rbufp->page + (int) bp[ndx + 1]; 68046366Sbostic val->size = bp[ndx] - bp[ndx + 1]; 68146366Sbostic } 68246366Sbostic break; 68346366Sbostic case HASH_PUT: 68446457Sbostic if ((__delpair (rbufp, ndx)) || (__addel (rbufp, key, val)) ) { 68546457Sbostic save_bufp->flags &= ~BUF_PIN; 68646457Sbostic return(ERROR); 68746457Sbostic } 68846366Sbostic break; 68946366Sbostic case HASH_DELETE: 69046366Sbostic if (__delpair (rbufp, ndx))return(ERROR); 69146366Sbostic break; 69246366Sbostic } 69346457Sbostic save_bufp->flags &= ~BUF_PIN; 69446366Sbostic return (SUCCESS); 69546366Sbostic } 69646366Sbostic 69746366Sbostic static int 69846366Sbostic hash_seq(dbp, key, data, flag) 69946366Sbostic DB *dbp; 70046366Sbostic DBT *key, *data; 70146366Sbostic int flag; 70246366Sbostic { 70346366Sbostic register int bucket; 70446366Sbostic register BUFHEAD *bufp; 70546457Sbostic BUFHEAD *save_bufp; 70646366Sbostic u_short *bp; 70746366Sbostic u_short ndx; 70846366Sbostic u_short pageno; 70946366Sbostic 71046366Sbostic if ( !dbp ) { 71146366Sbostic return (ERROR); 71246366Sbostic } 71346366Sbostic 71446366Sbostic hashp = (HTAB *)dbp->internal; 71546366Sbostic if ( hashp->flags & O_WRONLY ) { 71646366Sbostic errno = EBADF; 71746366Sbostic hashp->errno = errno; 71846366Sbostic return ( ERROR ); 71946366Sbostic } 72046366Sbostic #ifdef HASH_STATISTICS 72146366Sbostic hash_accesses++; 72246366Sbostic #endif 72346366Sbostic 72446366Sbostic if ( (hashp->cbucket < 0) || (flag == R_FIRST) ) { 72546366Sbostic hashp->cbucket = 0; 72646366Sbostic hashp->cndx = 1; 72746366Sbostic hashp->cpage = NULL; 72846366Sbostic } 72946366Sbostic 73046366Sbostic if ( !(bufp = hashp->cpage) ) { 73146366Sbostic for (bucket = hashp->cbucket; 73246366Sbostic bucket <= hashp->MAX_BUCKET; 73346366Sbostic bucket++, hashp->cndx = 1 ) { 73446366Sbostic 73546366Sbostic bufp = __get_buf ( bucket, NULL, 0 ); 73646366Sbostic if (!bufp) return(ERROR); 73746366Sbostic hashp->cpage = bufp; 73846366Sbostic bp = (u_short *)bufp->page; 73946366Sbostic if (bp[0]) break; 74046366Sbostic } 74146366Sbostic hashp->cbucket = bucket; 74246366Sbostic if ( hashp->cbucket > hashp->MAX_BUCKET ) { 74346366Sbostic hashp->cbucket = -1; 74446366Sbostic return ( ABNORMAL ); 74546366Sbostic } 74646366Sbostic } else { 74746366Sbostic bp = (u_short *)hashp->cpage->page; 74846366Sbostic } 74946366Sbostic 75046366Sbostic #ifdef DEBUG 75146366Sbostic assert (bp); 75246366Sbostic assert (bufp); 75346366Sbostic #endif 75446366Sbostic while ( bp[hashp->cndx+1] == OVFLPAGE ) { 75546366Sbostic bufp = hashp->cpage = __get_buf ( bp[hashp->cndx], bufp, 0 ); 75646366Sbostic if (!bufp) return(ERROR); 75746366Sbostic bp = (u_short *)(bufp->page); 75846366Sbostic hashp->cndx = 1; 75946366Sbostic } 76046366Sbostic ndx = hashp->cndx; 76146366Sbostic if (bp[ndx+1] < REAL_KEY) { 76246366Sbostic if (__big_keydata(bufp, ndx, key, data, 1)) { 76346366Sbostic return(ERROR); 76446366Sbostic } 76546366Sbostic } else { 76646366Sbostic key->data = hashp->cpage->page + bp[ndx]; 76746366Sbostic key->size = (ndx > 1 ? bp[ndx-1] : hashp->BSIZE) - bp[ndx]; 76846366Sbostic data->data = hashp->cpage->page + bp[ndx + 1]; 76946366Sbostic data->size = bp[ndx] - bp[ndx + 1]; 77046366Sbostic ndx += 2; 77146366Sbostic if ( ndx > bp[0] ) { 77246366Sbostic hashp->cpage = NULL; 77346366Sbostic hashp->cbucket++; 77446366Sbostic hashp->cndx=1; 77546366Sbostic } else hashp->cndx = ndx; 77646366Sbostic } 77746366Sbostic return (SUCCESS); 77846366Sbostic } 77946366Sbostic 78046366Sbostic /********************************* UTILITIES ************************/ 78146366Sbostic /* 78246366Sbostic 0 ==> OK 78346366Sbostic -1 ==> Error 78446366Sbostic */ 78546366Sbostic extern int 78646366Sbostic __expand_table() 78746366Sbostic { 78846366Sbostic int old_bucket, new_bucket; 78946366Sbostic int new_segnum; 79046366Sbostic int dirsize; 79146366Sbostic int spare_ndx; 79246366Sbostic register char **old, **new; 79346366Sbostic #ifdef HASH_STATISTICS 79446366Sbostic hash_expansions++; 79546366Sbostic #endif 79646366Sbostic 79746366Sbostic new_bucket = ++hashp->MAX_BUCKET; 79846366Sbostic old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK); 79946366Sbostic 80046366Sbostic new_segnum = new_bucket >> hashp->SSHIFT; 80146366Sbostic 80246366Sbostic /* Check if we need a new segment */ 80346366Sbostic if ( new_segnum >= hashp->nsegs ) { 80446366Sbostic 80546366Sbostic /* Check if we need to expand directory */ 80646366Sbostic if ( new_segnum >= hashp->DSIZE ) { 80746366Sbostic 80846366Sbostic /* Reallocate directory */ 80946366Sbostic dirsize = hashp->DSIZE * sizeof ( SEGMENT * ); 81046366Sbostic if (!hash_realloc ( &hashp->dir, dirsize, (dirsize << 1 ) )) { 81146366Sbostic return(-1); 81246366Sbostic } 81346366Sbostic hashp->DSIZE = dirsize << 1; 81446366Sbostic } 81546366Sbostic if (!(hashp->dir[new_segnum] = 81646366Sbostic (SEGMENT)calloc ( hashp->SGSIZE, sizeof(SEGMENT)))) { 81746366Sbostic return(-1); 81846366Sbostic } 81946366Sbostic hashp->exsegs++; 82046366Sbostic hashp->nsegs++; 82146366Sbostic } 82246366Sbostic 82346366Sbostic /* 82446366Sbostic If the split point is increasing (MAX_BUCKET's log 82546366Sbostic base 2 increases), we need to copy the current contents 82646366Sbostic of the spare split bucket to the next bucket 82746366Sbostic */ 82846366Sbostic spare_ndx = __log2(hashp->MAX_BUCKET); 82946366Sbostic if ( spare_ndx != (__log2(hashp->MAX_BUCKET - 1))) { 83046366Sbostic hashp->SPARES[spare_ndx] = hashp->SPARES[spare_ndx-1]; 83146366Sbostic } 83246366Sbostic 83346366Sbostic if ( new_bucket > hashp->HIGH_MASK ) { 83446366Sbostic /* Starting a new doubling */ 83546366Sbostic hashp->LOW_MASK = hashp->HIGH_MASK; 83646366Sbostic hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK; 83746366Sbostic } 83846366Sbostic 83946366Sbostic /* 84046366Sbostic * Relocate records to the new bucket 84146366Sbostic */ 84246366Sbostic return(__split_page ( old_bucket, new_bucket )); 84346366Sbostic } 84446366Sbostic /* 84546366Sbostic If realloc guarantees that the pointer is not destroyed 84646366Sbostic if the realloc fails, then this routine can go away 84746366Sbostic */ 84846366Sbostic static char * 84946366Sbostic hash_realloc ( p_ptr, oldsize, newsize ) 85046366Sbostic char **p_ptr; 85146366Sbostic int oldsize; 85246366Sbostic int newsize; 85346366Sbostic { 85446366Sbostic register char *p; 85546366Sbostic 85646366Sbostic if (p = (char *)malloc ( newsize ) ) { 85746366Sbostic bcopy ( *p_ptr, p, oldsize ); 85846366Sbostic bzero ( *p_ptr + oldsize, newsize-oldsize ); 85946366Sbostic free(*p_ptr); 86046366Sbostic *p_ptr = p; 86146366Sbostic } 86246366Sbostic return (p); 86346366Sbostic } 86446366Sbostic 86546366Sbostic extern int 86646366Sbostic __call_hash ( k, len ) 86746366Sbostic char *k; 86846366Sbostic int len; 86946366Sbostic { 87046366Sbostic int n, bucket; 87146366Sbostic n = hashp->hash(k, len); 87246366Sbostic 87346366Sbostic bucket = n & hashp->HIGH_MASK; 87446366Sbostic if ( bucket > hashp->MAX_BUCKET ) { 87546366Sbostic bucket = bucket & hashp->LOW_MASK; 87646366Sbostic } 87746366Sbostic 87846366Sbostic return(bucket); 87946366Sbostic } 88046366Sbostic 88146366Sbostic /* 88246366Sbostic Allocate segment table. On error, destroy the table 88346366Sbostic and set errno. 88446366Sbostic 88546366Sbostic Returns 0 on success 88646366Sbostic */ 88746366Sbostic static int 88846366Sbostic alloc_segs ( nsegs ) 88946366Sbostic int nsegs; 89046366Sbostic { 89146366Sbostic register int i; 89246366Sbostic register SEGMENT store; 89346366Sbostic 89446366Sbostic int save_errno; 89546366Sbostic 89646366Sbostic if (!(hashp->dir = (SEGMENT *)calloc(hashp->DSIZE, sizeof(SEGMENT *)))) { 89746366Sbostic save_errno = errno; 89846366Sbostic (void)hdestroy(); 89946366Sbostic errno = save_errno; 90046366Sbostic return(-1); 90146366Sbostic } 90246366Sbostic 90346366Sbostic /* Allocate segments */ 90446366Sbostic store = (SEGMENT)calloc ( nsegs << hashp->SSHIFT, sizeof (SEGMENT) ); 90546366Sbostic if ( !store ) { 90646366Sbostic save_errno = errno; 90746366Sbostic (void)hdestroy(); 90846366Sbostic errno = save_errno; 90946366Sbostic return(-1); 91046366Sbostic } 91146366Sbostic 91246366Sbostic for ( i=0; i < nsegs; i++, hashp->nsegs++ ) { 91346366Sbostic hashp->dir[i] = &store[i<<hashp->SSHIFT]; 91446366Sbostic } 91546366Sbostic return(0); 91646366Sbostic } 91746366Sbostic 91846366Sbostic /* 91946366Sbostic Hashp->hdr needs to be byteswapped. 92046366Sbostic */ 92146366Sbostic static void 92246366Sbostic swap_header_copy ( srcp, destp ) 92346366Sbostic HASHHDR *srcp; 92446366Sbostic HASHHDR *destp; 92546366Sbostic { 92646366Sbostic int i; 92746366Sbostic 92846366Sbostic BLSWAP_COPY(srcp->magic, destp->magic); 92946366Sbostic BLSWAP_COPY(srcp->version, destp->version); 93046366Sbostic BLSWAP_COPY(srcp->lorder, destp->lorder); 93146366Sbostic BLSWAP_COPY(srcp->bsize, destp->bsize); 93246366Sbostic BLSWAP_COPY(srcp->bshift, destp->bshift); 93346366Sbostic BLSWAP_COPY(srcp->dsize, destp->dsize); 93446366Sbostic BLSWAP_COPY(srcp->ssize, destp->ssize); 93546366Sbostic BLSWAP_COPY(srcp->sshift, destp->sshift); 93646366Sbostic BLSWAP_COPY(srcp->max_bucket, destp->max_bucket); 93746366Sbostic BLSWAP_COPY(srcp->high_mask, destp->high_mask); 93846366Sbostic BLSWAP_COPY(srcp->low_mask, destp->low_mask); 93946366Sbostic BLSWAP_COPY(srcp->ffactor, destp->ffactor); 94046366Sbostic BLSWAP_COPY(srcp->nkeys, destp->nkeys); 94146366Sbostic BLSWAP_COPY(srcp->hdrpages, destp->hdrpages); 94246366Sbostic BLSWAP_COPY(srcp->h_charkey, destp->h_charkey); 94346366Sbostic for ( i=0; i < NCACHED; i++ ) { 94446366Sbostic BLSWAP_COPY ( srcp->spares[i] , destp->spares[i]); 94546366Sbostic BSSWAP_COPY ( srcp->bitmaps[i] , destp->bitmaps[i]); 94646366Sbostic } 94746366Sbostic return; 94846366Sbostic } 94946366Sbostic 95046366Sbostic static void 95146366Sbostic swap_header ( ) 95246366Sbostic { 95346366Sbostic HASHHDR *hdrp; 95446366Sbostic int i; 95546366Sbostic 95646366Sbostic hdrp = &hashp->hdr; 95746366Sbostic 95846366Sbostic BLSWAP(hdrp->magic); 95946366Sbostic BLSWAP(hdrp->version); 96046366Sbostic BLSWAP(hdrp->lorder); 96146366Sbostic BLSWAP(hdrp->bsize); 96246366Sbostic BLSWAP(hdrp->bshift); 96346366Sbostic BLSWAP(hdrp->dsize); 96446366Sbostic BLSWAP(hdrp->ssize); 96546366Sbostic BLSWAP(hdrp->sshift); 96646366Sbostic BLSWAP(hdrp->max_bucket); 96746366Sbostic BLSWAP(hdrp->high_mask); 96846366Sbostic BLSWAP(hdrp->low_mask); 96946366Sbostic BLSWAP(hdrp->ffactor); 97046366Sbostic BLSWAP(hdrp->nkeys); 97146366Sbostic BLSWAP(hdrp->hdrpages); 97246366Sbostic BLSWAP(hdrp->h_charkey); 97346366Sbostic for ( i=0; i < NCACHED; i++ ) { 97446366Sbostic BLSWAP ( hdrp->spares[i] ); 97546366Sbostic BSSWAP ( hdrp->bitmaps[i] ); 97646366Sbostic } 97746366Sbostic return; 97846366Sbostic } 979