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