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