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*46950Sbostic static char sccsid[] = "@(#)hash.c 5.12 (Berkeley) 03/03/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 */ 4646366Sbostic extern void __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(); 6046366Sbostic extern 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; 18946366Sbostic hashp->mapp[0] = (u_long *)malloc(bpages<<hashp->BSHIFT); 19046366Sbostic if ( !hashp->mapp[0] ) { 19146366Sbostic RETURN_ERROR(errno, error2); 19246366Sbostic } 19346366Sbostic for ( i = 0; i < bpages; i++ ) { 19446366Sbostic hashp->mapp[i] = &hashp->mapp[0][i<<hashp->BSHIFT]; 19546366Sbostic if (__get_page ((char *)hashp->mapp[i], 19646366Sbostic hashp->BITMAPS[i], 0, 1, 1)) { 19746366Sbostic RETURN_ERROR(errno, error2); 19846366Sbostic } 19946366Sbostic } 20046366Sbostic 20146366Sbostic } 20246366Sbostic 20346366Sbostic /* Initialize Buffer Manager */ 20446366Sbostic if ( info && info->ncached ) { 20546366Sbostic __buf_init (info->ncached); 20646366Sbostic } else { 20746366Sbostic __buf_init (DEF_BUFSIZE); 20846366Sbostic } 20946366Sbostic 21046366Sbostic hashp->new_file = new_table; 21146457Sbostic hashp->save_file = file && !(hashp->flags&O_RDONLY); 21246366Sbostic hashp->cbucket = -1; 21346366Sbostic if ( !(dbp = (DB *)malloc ( sizeof (DB) )) ) { 21446366Sbostic save_errno = errno; 21546366Sbostic hdestroy(); 21646366Sbostic errno = save_errno; 21746366Sbostic return ( NULL ); 21846366Sbostic } 21946366Sbostic dbp->internal = (char *)hashp; 22046366Sbostic dbp->close = hash_close; 22146534Sbostic dbp->del = hash_delete; 22246366Sbostic dbp->get = hash_get; 22346366Sbostic dbp->put = hash_put; 22446366Sbostic dbp->seq = hash_seq; 22546366Sbostic dbp->sync = hash_sync; 22646366Sbostic 22746366Sbostic #ifdef DEBUG 22846366Sbostic (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", 22946366Sbostic "init_htab:", 23046366Sbostic "TABLE POINTER ", hashp, 23146366Sbostic "BUCKET SIZE ", hashp->BSIZE, 23246366Sbostic "BUCKET SHIFT ", hashp->BSHIFT, 23346366Sbostic "DIRECTORY SIZE ", hashp->DSIZE, 23446366Sbostic "SEGMENT SIZE ", hashp->SGSIZE, 23546366Sbostic "SEGMENT SHIFT ", hashp->SSHIFT, 23646366Sbostic "FILL FACTOR ", hashp->FFACTOR, 23746366Sbostic "MAX BUCKET ", hashp->MAX_BUCKET, 23846366Sbostic "HIGH MASK ", hashp->HIGH_MASK, 23946366Sbostic "LOW MASK ", hashp->LOW_MASK, 24046366Sbostic "NSEGS ", hashp->nsegs, 24146366Sbostic "NKEYS ", hashp->NKEYS ); 24246366Sbostic #endif 24346366Sbostic #ifdef HASH_STATISTICS 24446366Sbostic hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0; 24546366Sbostic #endif 24646366Sbostic return (dbp); 24746366Sbostic 24846366Sbostic error2: 24946366Sbostic (void)hdestroy(); 25046366Sbostic errno = save_errno; 25146366Sbostic return ( (DB *)NULL ); 25246366Sbostic 25346366Sbostic error1: 25446366Sbostic (void) close ( hashp->fp ); 25546366Sbostic 25646366Sbostic error0: 25746366Sbostic free ( hashp ); 25846366Sbostic errno = save_errno; 25946366Sbostic return ( (DB *) NULL ); 26046366Sbostic } 26146366Sbostic 26246366Sbostic static int 26346366Sbostic hash_close (dbp) 26446366Sbostic DB *dbp; 26546366Sbostic { 26646457Sbostic int retval; 26746457Sbostic 26846366Sbostic if ( !dbp ) { 26946366Sbostic return(ERROR); 27046366Sbostic } 27146366Sbostic hashp = (HTAB *)dbp->internal; 27246457Sbostic retval = hdestroy(); 27346457Sbostic (void)free ( dbp ); 27446457Sbostic return ( retval ); 27546366Sbostic } 27646366Sbostic 27746366Sbostic 27846366Sbostic /************************** LOCAL CREATION ROUTINES **********************/ 27946366Sbostic static HTAB * 28046366Sbostic init_hash( info ) 28146366Sbostic HASHINFO *info; 28246366Sbostic { 28346366Sbostic int nelem; 28446366Sbostic 28546366Sbostic nelem = 1; 28646366Sbostic 28746366Sbostic hashp->LORDER = BYTE_ORDER; 28846366Sbostic hashp->BSIZE = DEF_BUCKET_SIZE; 28946366Sbostic hashp->BSHIFT = DEF_BUCKET_SHIFT; 29046366Sbostic hashp->SGSIZE = DEF_SEGSIZE; 29146366Sbostic hashp->SSHIFT = DEF_SEGSIZE_SHIFT; 29246366Sbostic hashp->DSIZE = DEF_DIRSIZE; 29346366Sbostic hashp->FFACTOR = DEF_FFACTOR; 29446366Sbostic hashp->hash = default_hash; 29546366Sbostic bzero (hashp->SPARES, sizeof (hashp->SPARES)); 29646366Sbostic 29746366Sbostic if ( info ) { 29846366Sbostic if ( info->bsize ) { 29946366Sbostic /* Round pagesize up to power of 2 */ 30046366Sbostic hashp->BSHIFT = __log2(info->bsize); 30146366Sbostic hashp->BSIZE = 1 << hashp->BSHIFT; 30246366Sbostic if ( hashp->BSIZE > MAX_BSIZE ) { 30346366Sbostic errno = EINVAL; 30446366Sbostic return(0); 30546366Sbostic } 30646366Sbostic } 30746366Sbostic if ( info->ffactor ) hashp->FFACTOR = info->ffactor; 30846366Sbostic if ( info->hash ) hashp->hash = info->hash; 30946366Sbostic if ( info->nelem ) nelem = info->nelem; 31046366Sbostic if ( info->lorder ) { 31146366Sbostic if ( info->lorder != BIG_ENDIAN && 31246366Sbostic info->lorder != LITTLE_ENDIAN) { 31346366Sbostic errno = EINVAL; 31446366Sbostic return(0); 31546366Sbostic } 31646366Sbostic hashp->LORDER = info->lorder; 31746366Sbostic } 31846366Sbostic } 31946366Sbostic 32046366Sbostic /* init_htab should destroy the table and set errno if it fails */ 32146366Sbostic if ( init_htab ( nelem ) ) { 32246366Sbostic return(0); 32346366Sbostic } else { 32446366Sbostic return(hashp); 32546366Sbostic } 32646366Sbostic } 32746366Sbostic 32846366Sbostic /* 32946366Sbostic This calls alloc_segs which may run out of memory. 33046366Sbostic Alloc_segs will destroy the table and set errno, 33146366Sbostic so we just pass the error information along. 33246366Sbostic 33346366Sbostic Returns 0 on No Error 33446366Sbostic 33546366Sbostic */ 33646366Sbostic static int 33746366Sbostic init_htab ( nelem ) 33846366Sbostic int nelem; 33946366Sbostic { 34046366Sbostic register SEGMENT *segp; 34146366Sbostic register int nbuckets; 34246366Sbostic register int nsegs; 34346366Sbostic int l2; 34446366Sbostic 34546366Sbostic /* 34646366Sbostic * Divide number of elements by the fill factor and determine a desired 34746366Sbostic * number of buckets. Allocate space for the next greater power of 34846366Sbostic * two number of buckets 34946366Sbostic */ 35046366Sbostic nelem = (nelem - 1) / hashp->FFACTOR + 1; 35146366Sbostic 35246366Sbostic l2 = __log2(nelem); 35346366Sbostic nbuckets = 1 << l2; 35446366Sbostic nbuckets = MAX ( nbuckets, 2 ); 35546366Sbostic 35646366Sbostic hashp->SPARES[l2] = l2 + 1; 35746366Sbostic hashp->SPARES[l2+1] = l2 + 1; 35846366Sbostic /* 35946366Sbostic First bitmap page is at: 36046366Sbostic splitpoint l2 36146366Sbostic page offset 1 36246366Sbostic */ 36346366Sbostic __init_bitmap ( OADDR_OF(l2,1), l2+1, 0 ); 36446366Sbostic 36546366Sbostic hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1; 36646366Sbostic hashp->HIGH_MASK = (nbuckets << 1) - 1; 36746366Sbostic hashp->HDRPAGES = ((MAX(sizeof(HASHHDR),MINHDRSIZE) - 1) >> 36846366Sbostic hashp->BSHIFT) + 1; 36946366Sbostic 37046366Sbostic nsegs = (nbuckets - 1) / hashp->SGSIZE + 1; 37146366Sbostic nsegs = 1 << __log2(nsegs); 37246366Sbostic 37346366Sbostic if ( nsegs > hashp->DSIZE ) { 37446366Sbostic hashp->DSIZE = nsegs; 37546366Sbostic } 37646366Sbostic 37746366Sbostic return (alloc_segs ( nsegs ) ); 37846366Sbostic } 37946366Sbostic 38046366Sbostic /********************** DESTROY/CLOSE ROUTINES ************************/ 38146366Sbostic 38246366Sbostic /* 38346366Sbostic Flushes any changes to the file if necessary and 38446366Sbostic destroys the hashp structure, freeing all allocated 38546366Sbostic space. 38646366Sbostic */ 38746366Sbostic static int 38846366Sbostic hdestroy() 38946366Sbostic { 39046366Sbostic int save_errno; 39146366Sbostic int i; 39246503Sbostic u_long **mapp; 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 } 43046503Sbostic 43146503Sbostic /* Free Initial Bigmaps */ 43246503Sbostic if ( hashp->nmaps ) { 43346503Sbostic (void)free(hashp->mapp[0]); 43446503Sbostic } 43546503Sbostic 43646503Sbostic /* Free extra bitmaps */ 43746503Sbostic for ( mapp = &hashp->mapp[hashp->nmaps]; 43846503Sbostic hashp->exmaps--; 43946503Sbostic mapp++ ) { 44046503Sbostic (void) free ( *mapp ); 44146503Sbostic } 44246503Sbostic 44346457Sbostic if ( hashp->fp != -1 ) { 44446457Sbostic (void)close (hashp->fp); 44546457Sbostic } 44646366Sbostic (void)free(hashp); 44746366Sbostic hashp = NULL; 44846366Sbostic } 44946366Sbostic if ( save_errno ) { 45046366Sbostic errno = save_errno; 45146366Sbostic return(ERROR); 45246366Sbostic } else { 45346366Sbostic return(SUCCESS); 45446366Sbostic } 45546366Sbostic } 45646366Sbostic 45746366Sbostic /* 45846366Sbostic Write modified pages to disk 45946366Sbostic 0 == OK 46046366Sbostic -1 ERROR 46146366Sbostic */ 46246366Sbostic static int 46346366Sbostic hash_sync(dbp) 46446366Sbostic DB *dbp; 46546366Sbostic { 46646366Sbostic if ( !dbp ) { 46746366Sbostic return (ERROR); 46846366Sbostic } 46946366Sbostic 47046366Sbostic hashp = (HTAB *)dbp->internal; 47146366Sbostic 47246366Sbostic if (!hashp->save_file) return(0); 47346366Sbostic if ( __buf_free ( 0, 1 ) || flush_meta()) { 47446366Sbostic return(ERROR); 47546366Sbostic } 47646366Sbostic hashp->new_file = 0; 47746366Sbostic return(0); 47846366Sbostic } 47946366Sbostic 48046366Sbostic /* 48146366Sbostic 0 == OK 48246366Sbostic -1 indicates that errno should be set 48346366Sbostic */ 48446366Sbostic static int 48546366Sbostic flush_meta() 48646366Sbostic { 48746366Sbostic int fp; 48846366Sbostic int hdrsize; 48946366Sbostic int i; 49046366Sbostic int wsize; 49146366Sbostic HASHHDR *whdrp; 49246366Sbostic HASHHDR whdr; 49346366Sbostic 49446366Sbostic if (!hashp->save_file) return(0); 49546366Sbostic hashp->MAGIC = HASHMAGIC; 49646366Sbostic hashp->VERSION = VERSION_NO; 49746366Sbostic hashp->H_CHARKEY = hashp->hash ( CHARKEY, sizeof(CHARKEY)); 49846366Sbostic 49946366Sbostic fp = hashp->fp; 50046366Sbostic whdrp = &hashp->hdr; 50146366Sbostic #if BYTE_ORDER == LITTLE_ENDIAN 50246366Sbostic whdrp = &whdr; 50346366Sbostic swap_header_copy( &hashp->hdr, whdrp ); 50446366Sbostic #endif 50546500Sbostic if ( (lseek (fp, 0, SEEK_SET) == -1 ) || 50646366Sbostic ((wsize = write ( fp, whdrp, sizeof(HASHHDR))) == -1)) { 50746366Sbostic return(-1); 50846366Sbostic } else if ( wsize != sizeof(HASHHDR) ) { 50946366Sbostic errno = EFTYPE; 51046366Sbostic hashp->errno = errno; 51146366Sbostic return(-1); 51246366Sbostic } 51346366Sbostic for ( i = 0; i < NCACHED; i++ ) { 51446366Sbostic if ( hashp->mapp[i] ) { 51546366Sbostic if (!__put_page((char *)hashp->mapp[i], 51646366Sbostic hashp->BITMAPS[i], 0, 1)){ 51746366Sbostic return(-1); 51846366Sbostic } 51946366Sbostic } 52046366Sbostic } 52146366Sbostic return(0); 52246366Sbostic } 52346366Sbostic /*******************************SEARCH ROUTINES *****************************/ 52446366Sbostic /* 52546366Sbostic All the access routines return 52646366Sbostic 0 on SUCCESS 52746366Sbostic 1 to indicate an external ERROR (i.e. key not found, etc) 52846366Sbostic -1 to indicate an internal ERROR (i.e. out of memory, etc) 52946366Sbostic */ 53046366Sbostic static int 53146571Sbostic hash_get ( dbp, key, data, flag ) 53246366Sbostic DB *dbp; 53346571Sbostic DBT *key, *data; 53446571Sbostic u_long flag; 53546366Sbostic { 53646571Sbostic #ifdef lint 53746571Sbostic flag = flag; 53846571Sbostic #endif 53946571Sbostic 54046366Sbostic if ( !dbp ) { 54146366Sbostic return (ERROR); 54246366Sbostic } 54346366Sbostic hashp = (HTAB *)dbp->internal; 54446366Sbostic if ( hashp->flags & O_WRONLY ) { 54546366Sbostic errno = EBADF; 54646366Sbostic hashp->errno = errno; 54746366Sbostic return ( ERROR ); 54846366Sbostic } 54946366Sbostic return ( hash_access ( HASH_GET, key, data ) ); 55046366Sbostic } 55146366Sbostic 55246366Sbostic static int 55346366Sbostic hash_put ( dbp, key, data, flag ) 55446366Sbostic DB *dbp; 55546366Sbostic DBT *key, *data; 55646571Sbostic u_long flag; 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 if ( flag == R_NOOVERWRITE ) { 56846366Sbostic return ( hash_access ( HASH_PUTNEW, key, data ) ); 56946366Sbostic } else { 57046366Sbostic return ( hash_access ( HASH_PUT, key, data ) ); 57146366Sbostic } 57246366Sbostic } 57346366Sbostic 57446366Sbostic static int 57546571Sbostic hash_delete ( dbp, key, flag ) 57646366Sbostic DB *dbp; 57746366Sbostic DBT *key; 57846571Sbostic u_long flag; /* Ignored */ 57946366Sbostic { 58046571Sbostic #ifdef lint 58146571Sbostic flag = flag; 58246571Sbostic #endif 58346366Sbostic if ( !dbp ) { 58446366Sbostic return (ERROR); 58546366Sbostic } 58646366Sbostic hashp = (HTAB *)dbp->internal; 58746366Sbostic if ( (hashp->flags & O_ACCMODE) == O_RDONLY ) { 58846366Sbostic errno = EBADF; 58946366Sbostic hashp->errno = errno; 59046366Sbostic return ( ERROR ); 59146366Sbostic } 59246366Sbostic return ( hash_access ( HASH_DELETE, key, NULL ) ); 59346366Sbostic } 59446366Sbostic 59546366Sbostic /* 59646366Sbostic Assume that hashp has been set in wrapper routine 59746366Sbostic */ 59846366Sbostic static int 59946366Sbostic hash_access(action, key, val) 60046366Sbostic ACTION action; 60146366Sbostic DBT *key, *val; 60246366Sbostic { 60346366Sbostic register BUFHEAD *rbufp; 60446366Sbostic register u_short *bp; 60546366Sbostic register int ndx; 60646366Sbostic register int n; 60746366Sbostic register int off = hashp->BSIZE; 60846366Sbostic register int size; 60946366Sbostic register char *kp; 61046366Sbostic BUFHEAD *bufp; 61146457Sbostic BUFHEAD *save_bufp; 61246366Sbostic u_short pageno; 61346366Sbostic 61446366Sbostic #ifdef HASH_STATISTICS 61546366Sbostic hash_accesses++; 61646366Sbostic #endif 61746366Sbostic 61846366Sbostic size = key->size; 619*46950Sbostic kp = (char *)key->data; 62046457Sbostic rbufp = __get_buf ( __call_hash(kp, size), NULL, 0 ); 62146366Sbostic if ( !rbufp ) return(ERROR); 62246457Sbostic save_bufp = rbufp; 62346366Sbostic 62446457Sbostic /* Pin the bucket chain */ 62546457Sbostic rbufp->flags |= BUF_PIN; 62646366Sbostic for ( bp = (u_short *)rbufp->page, n = *bp++, ndx = 1; ndx < n; ) { 62746366Sbostic if ( bp[1] >= REAL_KEY ) { 62846366Sbostic /* Real key/data pair */ 62946366Sbostic if (size == off - *bp && 63046366Sbostic bcmp(kp, rbufp->page + *bp, size) == 0) { 63146366Sbostic goto found; 63246366Sbostic } 63346366Sbostic off = bp[1]; 63446366Sbostic #ifdef HASH_STATISTICS 63546366Sbostic hash_collisions++; 63646366Sbostic #endif 63746366Sbostic bp += 2; 63846366Sbostic ndx += 2; 63946366Sbostic } else if ( bp[1] == OVFLPAGE ) { 64046366Sbostic rbufp = __get_buf ( *bp, rbufp, 0 ); 64146457Sbostic if ( !rbufp ) { 64246457Sbostic save_bufp->flags &= ~BUF_PIN; 64346457Sbostic return (ERROR); 64446457Sbostic } 64546366Sbostic /* FOR LOOP INIT */ 64646366Sbostic bp = (u_short *)rbufp->page; 64746366Sbostic n = *bp++; 64846366Sbostic ndx = 1; 64946366Sbostic off = hashp->BSIZE; 65046366Sbostic } else if ( bp[1] < REAL_KEY ) { 65146366Sbostic if ( (ndx = __find_bigpair(rbufp, ndx, kp, size )) > 0 ) { 65246366Sbostic goto found; 65346366Sbostic } else if ( ndx == -2 ) { 65446366Sbostic bufp = rbufp; 65546366Sbostic if ( !(pageno = __find_last_page ( &bufp )) ) { 65646366Sbostic ndx = 0; 65746366Sbostic rbufp = bufp; 65846366Sbostic break; /* FOR */ 65946366Sbostic } 66046366Sbostic rbufp = __get_buf ( pageno, bufp, 0 ); 66146457Sbostic if ( !rbufp ) { 66246457Sbostic save_bufp->flags &= ~BUF_PIN; 66346457Sbostic return (ERROR); 66446457Sbostic } 66546366Sbostic /* FOR LOOP INIT */ 66646366Sbostic bp = (u_short *)rbufp->page; 66746366Sbostic n = *bp++; 66846366Sbostic ndx = 1; 66946366Sbostic off = hashp->BSIZE; 67046366Sbostic } else { 67146457Sbostic save_bufp->flags &= ~BUF_PIN; 67246366Sbostic return(ERROR); 67346366Sbostic } 67446366Sbostic } 67546366Sbostic } 67646366Sbostic 67746366Sbostic /* Not found */ 67846366Sbostic switch ( action ) { 67946366Sbostic case HASH_PUT: 68046366Sbostic case HASH_PUTNEW: 68146366Sbostic if (__addel(rbufp, key, val)) { 68246457Sbostic save_bufp->flags &= ~BUF_PIN; 68346366Sbostic return(ERROR); 68446366Sbostic } else { 68546457Sbostic save_bufp->flags &= ~BUF_PIN; 68646366Sbostic return(SUCCESS); 68746366Sbostic } 68846366Sbostic case HASH_GET: 68946366Sbostic case HASH_DELETE: 69046366Sbostic default: 69146457Sbostic save_bufp->flags &= ~BUF_PIN; 69246366Sbostic return(ABNORMAL); 69346366Sbostic } 69446366Sbostic 69546366Sbostic found: 69646366Sbostic switch (action) { 69746366Sbostic case HASH_PUTNEW: 69846457Sbostic save_bufp->flags &= ~BUF_PIN; 69946366Sbostic return(ABNORMAL); 70046366Sbostic case HASH_GET: 70146366Sbostic bp = (u_short *)rbufp->page; 70246366Sbostic if (bp[ndx+1] < REAL_KEY) __big_return(rbufp, ndx, val, 0); 70346366Sbostic else { 704*46950Sbostic val->data = (u_char *)rbufp->page + (int) bp[ndx + 1]; 70546366Sbostic val->size = bp[ndx] - bp[ndx + 1]; 70646366Sbostic } 70746366Sbostic break; 70846366Sbostic case HASH_PUT: 70946457Sbostic if ((__delpair (rbufp, ndx)) || (__addel (rbufp, key, val)) ) { 71046457Sbostic save_bufp->flags &= ~BUF_PIN; 71146457Sbostic return(ERROR); 71246457Sbostic } 71346366Sbostic break; 71446366Sbostic case HASH_DELETE: 71546366Sbostic if (__delpair (rbufp, ndx))return(ERROR); 71646366Sbostic break; 71746366Sbostic } 71846457Sbostic save_bufp->flags &= ~BUF_PIN; 71946366Sbostic return (SUCCESS); 72046366Sbostic } 72146366Sbostic 72246366Sbostic static int 72346366Sbostic hash_seq(dbp, key, data, flag) 72446366Sbostic DB *dbp; 72546366Sbostic DBT *key, *data; 72646571Sbostic u_long flag; 72746366Sbostic { 72846366Sbostic register int bucket; 72946366Sbostic register BUFHEAD *bufp; 73046457Sbostic BUFHEAD *save_bufp; 73146366Sbostic u_short *bp; 73246366Sbostic u_short ndx; 73346366Sbostic u_short pageno; 73446366Sbostic 73546366Sbostic if ( !dbp ) { 73646366Sbostic return (ERROR); 73746366Sbostic } 73846366Sbostic 73946366Sbostic hashp = (HTAB *)dbp->internal; 74046366Sbostic if ( hashp->flags & O_WRONLY ) { 74146366Sbostic errno = EBADF; 74246366Sbostic hashp->errno = errno; 74346366Sbostic return ( ERROR ); 74446366Sbostic } 74546366Sbostic #ifdef HASH_STATISTICS 74646366Sbostic hash_accesses++; 74746366Sbostic #endif 74846366Sbostic 74946366Sbostic if ( (hashp->cbucket < 0) || (flag == R_FIRST) ) { 75046366Sbostic hashp->cbucket = 0; 75146366Sbostic hashp->cndx = 1; 75246366Sbostic hashp->cpage = NULL; 75346366Sbostic } 75446366Sbostic 75546366Sbostic if ( !(bufp = hashp->cpage) ) { 75646366Sbostic for (bucket = hashp->cbucket; 75746366Sbostic bucket <= hashp->MAX_BUCKET; 75846366Sbostic bucket++, hashp->cndx = 1 ) { 75946366Sbostic 76046366Sbostic bufp = __get_buf ( bucket, NULL, 0 ); 76146366Sbostic if (!bufp) return(ERROR); 76246366Sbostic hashp->cpage = bufp; 76346366Sbostic bp = (u_short *)bufp->page; 76446366Sbostic if (bp[0]) break; 76546366Sbostic } 76646366Sbostic hashp->cbucket = bucket; 76746366Sbostic if ( hashp->cbucket > hashp->MAX_BUCKET ) { 76846366Sbostic hashp->cbucket = -1; 76946366Sbostic return ( ABNORMAL ); 77046366Sbostic } 77146366Sbostic } else { 77246366Sbostic bp = (u_short *)hashp->cpage->page; 77346366Sbostic } 77446366Sbostic 77546366Sbostic #ifdef DEBUG 77646366Sbostic assert (bp); 77746366Sbostic assert (bufp); 77846366Sbostic #endif 77946366Sbostic while ( bp[hashp->cndx+1] == OVFLPAGE ) { 78046366Sbostic bufp = hashp->cpage = __get_buf ( bp[hashp->cndx], bufp, 0 ); 78146366Sbostic if (!bufp) return(ERROR); 78246366Sbostic bp = (u_short *)(bufp->page); 78346366Sbostic hashp->cndx = 1; 78446366Sbostic } 78546366Sbostic ndx = hashp->cndx; 78646366Sbostic if (bp[ndx+1] < REAL_KEY) { 78746366Sbostic if (__big_keydata(bufp, ndx, key, data, 1)) { 78846366Sbostic return(ERROR); 78946366Sbostic } 79046366Sbostic } else { 791*46950Sbostic key->data = (u_char *)hashp->cpage->page + bp[ndx]; 79246366Sbostic key->size = (ndx > 1 ? bp[ndx-1] : hashp->BSIZE) - bp[ndx]; 793*46950Sbostic data->data = (u_char *)hashp->cpage->page + bp[ndx + 1]; 79446366Sbostic data->size = bp[ndx] - bp[ndx + 1]; 79546366Sbostic ndx += 2; 79646366Sbostic if ( ndx > bp[0] ) { 79746366Sbostic hashp->cpage = NULL; 79846366Sbostic hashp->cbucket++; 79946366Sbostic hashp->cndx=1; 80046366Sbostic } else hashp->cndx = ndx; 80146366Sbostic } 80246366Sbostic return (SUCCESS); 80346366Sbostic } 80446366Sbostic 80546366Sbostic /********************************* UTILITIES ************************/ 80646366Sbostic /* 80746366Sbostic 0 ==> OK 80846366Sbostic -1 ==> Error 80946366Sbostic */ 81046366Sbostic extern int 81146366Sbostic __expand_table() 81246366Sbostic { 81346366Sbostic int old_bucket, new_bucket; 81446366Sbostic int new_segnum; 81546366Sbostic int dirsize; 81646366Sbostic int spare_ndx; 81746366Sbostic register char **old, **new; 81846366Sbostic #ifdef HASH_STATISTICS 81946366Sbostic hash_expansions++; 82046366Sbostic #endif 82146366Sbostic 82246366Sbostic new_bucket = ++hashp->MAX_BUCKET; 82346366Sbostic old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK); 82446366Sbostic 82546366Sbostic new_segnum = new_bucket >> hashp->SSHIFT; 82646366Sbostic 82746366Sbostic /* Check if we need a new segment */ 82846366Sbostic if ( new_segnum >= hashp->nsegs ) { 82946366Sbostic 83046366Sbostic /* Check if we need to expand directory */ 83146366Sbostic if ( new_segnum >= hashp->DSIZE ) { 83246366Sbostic 83346366Sbostic /* Reallocate directory */ 83446366Sbostic dirsize = hashp->DSIZE * sizeof ( SEGMENT * ); 83546366Sbostic if (!hash_realloc ( &hashp->dir, dirsize, (dirsize << 1 ) )) { 83646366Sbostic return(-1); 83746366Sbostic } 83846366Sbostic hashp->DSIZE = dirsize << 1; 83946366Sbostic } 84046366Sbostic if (!(hashp->dir[new_segnum] = 84146366Sbostic (SEGMENT)calloc ( hashp->SGSIZE, sizeof(SEGMENT)))) { 84246366Sbostic return(-1); 84346366Sbostic } 84446366Sbostic hashp->exsegs++; 84546366Sbostic hashp->nsegs++; 84646366Sbostic } 84746366Sbostic 84846366Sbostic /* 84946366Sbostic If the split point is increasing (MAX_BUCKET's log 85046366Sbostic base 2 increases), we need to copy the current contents 85146366Sbostic of the spare split bucket to the next bucket 85246366Sbostic */ 85346366Sbostic spare_ndx = __log2(hashp->MAX_BUCKET); 85446366Sbostic if ( spare_ndx != (__log2(hashp->MAX_BUCKET - 1))) { 85546366Sbostic hashp->SPARES[spare_ndx] = hashp->SPARES[spare_ndx-1]; 85646366Sbostic } 85746366Sbostic 85846366Sbostic if ( new_bucket > hashp->HIGH_MASK ) { 85946366Sbostic /* Starting a new doubling */ 86046366Sbostic hashp->LOW_MASK = hashp->HIGH_MASK; 86146366Sbostic hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK; 86246366Sbostic } 86346366Sbostic 86446366Sbostic /* 86546366Sbostic * Relocate records to the new bucket 86646366Sbostic */ 86746366Sbostic return(__split_page ( old_bucket, new_bucket )); 86846366Sbostic } 86946366Sbostic /* 87046366Sbostic If realloc guarantees that the pointer is not destroyed 87146366Sbostic if the realloc fails, then this routine can go away 87246366Sbostic */ 87346366Sbostic static char * 87446366Sbostic hash_realloc ( p_ptr, oldsize, newsize ) 87546366Sbostic char **p_ptr; 87646366Sbostic int oldsize; 87746366Sbostic int newsize; 87846366Sbostic { 87946366Sbostic register char *p; 88046366Sbostic 88146366Sbostic if (p = (char *)malloc ( newsize ) ) { 88246366Sbostic bcopy ( *p_ptr, p, oldsize ); 88346366Sbostic bzero ( *p_ptr + oldsize, newsize-oldsize ); 88446366Sbostic free(*p_ptr); 88546366Sbostic *p_ptr = p; 88646366Sbostic } 88746366Sbostic return (p); 88846366Sbostic } 88946366Sbostic 89046366Sbostic extern int 89146366Sbostic __call_hash ( k, len ) 89246366Sbostic char *k; 89346366Sbostic int len; 89446366Sbostic { 89546366Sbostic int n, bucket; 89646366Sbostic n = hashp->hash(k, len); 89746366Sbostic 89846366Sbostic bucket = n & hashp->HIGH_MASK; 89946366Sbostic if ( bucket > hashp->MAX_BUCKET ) { 90046366Sbostic bucket = bucket & hashp->LOW_MASK; 90146366Sbostic } 90246366Sbostic 90346366Sbostic return(bucket); 90446366Sbostic } 90546366Sbostic 90646366Sbostic /* 90746366Sbostic Allocate segment table. On error, destroy the table 90846366Sbostic and set errno. 90946366Sbostic 91046366Sbostic Returns 0 on success 91146366Sbostic */ 91246366Sbostic static int 91346366Sbostic alloc_segs ( nsegs ) 91446366Sbostic int nsegs; 91546366Sbostic { 91646366Sbostic register int i; 91746366Sbostic register SEGMENT store; 91846366Sbostic 91946366Sbostic int save_errno; 92046366Sbostic 92146366Sbostic if (!(hashp->dir = (SEGMENT *)calloc(hashp->DSIZE, sizeof(SEGMENT *)))) { 92246366Sbostic save_errno = errno; 92346366Sbostic (void)hdestroy(); 92446366Sbostic errno = save_errno; 92546366Sbostic return(-1); 92646366Sbostic } 92746366Sbostic 92846366Sbostic /* Allocate segments */ 92946366Sbostic store = (SEGMENT)calloc ( nsegs << hashp->SSHIFT, sizeof (SEGMENT) ); 93046366Sbostic if ( !store ) { 93146366Sbostic save_errno = errno; 93246366Sbostic (void)hdestroy(); 93346366Sbostic errno = save_errno; 93446366Sbostic return(-1); 93546366Sbostic } 93646366Sbostic 93746366Sbostic for ( i=0; i < nsegs; i++, hashp->nsegs++ ) { 93846366Sbostic hashp->dir[i] = &store[i<<hashp->SSHIFT]; 93946366Sbostic } 94046366Sbostic return(0); 94146366Sbostic } 94246366Sbostic 94346366Sbostic /* 94446366Sbostic Hashp->hdr needs to be byteswapped. 94546366Sbostic */ 94646366Sbostic static void 94746366Sbostic swap_header_copy ( srcp, destp ) 94846366Sbostic HASHHDR *srcp; 94946366Sbostic HASHHDR *destp; 95046366Sbostic { 95146366Sbostic int i; 95246366Sbostic 95346366Sbostic BLSWAP_COPY(srcp->magic, destp->magic); 95446366Sbostic BLSWAP_COPY(srcp->version, destp->version); 95546366Sbostic BLSWAP_COPY(srcp->lorder, destp->lorder); 95646366Sbostic BLSWAP_COPY(srcp->bsize, destp->bsize); 95746366Sbostic BLSWAP_COPY(srcp->bshift, destp->bshift); 95846366Sbostic BLSWAP_COPY(srcp->dsize, destp->dsize); 95946366Sbostic BLSWAP_COPY(srcp->ssize, destp->ssize); 96046366Sbostic BLSWAP_COPY(srcp->sshift, destp->sshift); 96146366Sbostic BLSWAP_COPY(srcp->max_bucket, destp->max_bucket); 96246366Sbostic BLSWAP_COPY(srcp->high_mask, destp->high_mask); 96346366Sbostic BLSWAP_COPY(srcp->low_mask, destp->low_mask); 96446366Sbostic BLSWAP_COPY(srcp->ffactor, destp->ffactor); 96546366Sbostic BLSWAP_COPY(srcp->nkeys, destp->nkeys); 96646366Sbostic BLSWAP_COPY(srcp->hdrpages, destp->hdrpages); 96746366Sbostic BLSWAP_COPY(srcp->h_charkey, destp->h_charkey); 96846366Sbostic for ( i=0; i < NCACHED; i++ ) { 96946366Sbostic BLSWAP_COPY ( srcp->spares[i] , destp->spares[i]); 97046366Sbostic BSSWAP_COPY ( srcp->bitmaps[i] , destp->bitmaps[i]); 97146366Sbostic } 97246366Sbostic return; 97346366Sbostic } 97446366Sbostic 97546366Sbostic static void 97646366Sbostic swap_header ( ) 97746366Sbostic { 97846366Sbostic HASHHDR *hdrp; 97946366Sbostic int i; 98046366Sbostic 98146366Sbostic hdrp = &hashp->hdr; 98246366Sbostic 98346366Sbostic BLSWAP(hdrp->magic); 98446366Sbostic BLSWAP(hdrp->version); 98546366Sbostic BLSWAP(hdrp->lorder); 98646366Sbostic BLSWAP(hdrp->bsize); 98746366Sbostic BLSWAP(hdrp->bshift); 98846366Sbostic BLSWAP(hdrp->dsize); 98946366Sbostic BLSWAP(hdrp->ssize); 99046366Sbostic BLSWAP(hdrp->sshift); 99146366Sbostic BLSWAP(hdrp->max_bucket); 99246366Sbostic BLSWAP(hdrp->high_mask); 99346366Sbostic BLSWAP(hdrp->low_mask); 99446366Sbostic BLSWAP(hdrp->ffactor); 99546366Sbostic BLSWAP(hdrp->nkeys); 99646366Sbostic BLSWAP(hdrp->hdrpages); 99746366Sbostic BLSWAP(hdrp->h_charkey); 99846366Sbostic for ( i=0; i < NCACHED; i++ ) { 99946366Sbostic BLSWAP ( hdrp->spares[i] ); 100046366Sbostic BSSWAP ( hdrp->bitmaps[i] ); 100146366Sbostic } 100246366Sbostic return; 100346366Sbostic } 1004