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