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