146372Sbostic /*- 246372Sbostic * Copyright (c) 1990 The Regents of the University of California. 346372Sbostic * All rights reserved. 446372Sbostic * 546372Sbostic * This code is derived from software contributed to Berkeley by 646372Sbostic * Margo Seltzer. 746372Sbostic * 846372Sbostic * %sccs.include.redist.c% 946372Sbostic */ 1046372Sbostic 1146372Sbostic #if defined(LIBC_SCCS) && !defined(lint) 12*46499Sbostic static char sccsid[] = "@(#)hash_page.c 5.4 (Berkeley) 02/21/91"; 1346372Sbostic #endif /* LIBC_SCCS and not lint */ 1446372Sbostic 1546372Sbostic /****************************************************************************** 1646372Sbostic PACKAGE: hashing 1746372Sbostic 1846372Sbostic DESCRIPTION: 1946372Sbostic Page manipulation for hashing package. 2046372Sbostic 2146372Sbostic ROUTINES: 2246372Sbostic External 2346372Sbostic __get_page 2446372Sbostic __add_ovflpage 2546372Sbostic Internal 2646372Sbostic overflow_page 2746372Sbostic open_temp 2846372Sbostic ******************************************************************************/ 2946372Sbostic 3046372Sbostic #include <sys/param.h> 3146372Sbostic #include <sys/file.h> 3246416Sbostic #include <signal.h> 3346372Sbostic #include <assert.h> 3446372Sbostic #include <errno.h> 3546372Sbostic #include <db.h> 3646372Sbostic #include <stdio.h> 3746372Sbostic #include "hash.h" 3846372Sbostic #include "page.h" 3946372Sbostic 4046372Sbostic /* Externals */ 4146372Sbostic /* buf.c */ 4246372Sbostic extern BUFHEAD *__get_buf(); 4346372Sbostic extern void __reclaim_buf(); 4446372Sbostic 4546372Sbostic /* big.c */ 4646372Sbostic extern int __big_split(); 4746372Sbostic extern int __big_insert(); 4846372Sbostic extern int __big_delete(); 4946372Sbostic extern int __find_bigpair(); 5046372Sbostic 5146372Sbostic /* dynahash.c */ 5246372Sbostic extern int __call_hash(); 5346372Sbostic extern int __expand_table(); 5446372Sbostic 5546372Sbostic /* my externals */ 5646372Sbostic extern int __get_page(); 5746372Sbostic extern BUFHEAD *__add_ovflpage(); 5846372Sbostic extern int __split_page(); 5946372Sbostic extern int __addel(); 6046372Sbostic 6146372Sbostic /* my internals */ 6246372Sbostic static u_short overflow_page(); 6346372Sbostic static int open_temp(); 6446372Sbostic static void squeeze_key(); 6546372Sbostic static void putpair(); 6646372Sbostic 6746372Sbostic #ifdef HASH_STATISTICS 6846372Sbostic extern long hash_accesses, hash_collisions, hash_expansions, hash_overflows; 6946372Sbostic #endif 7046372Sbostic #define PAGE_INIT(P) \ 7146372Sbostic { \ 7246372Sbostic ((u_short *)P)[0] = 0; \ 7346372Sbostic ((u_short *)P)[1] = hashp->BSIZE - 3 * sizeof(u_short); \ 7446372Sbostic ((u_short *)P)[2] = hashp->BSIZE; \ 7546372Sbostic } 7646372Sbostic 7746372Sbostic /* 7846372Sbostic This is called AFTER we have verified that there is room on the 7946372Sbostic page for the pair (PAIRFITS has returned true) so we go right 8046372Sbostic ahead and start moving stuff on. 8146372Sbostic */ 8246372Sbostic static void 8346372Sbostic putpair(p, key, val) 8446372Sbostic char *p; 8546372Sbostic DBT *key; 8646372Sbostic DBT *val; 8746372Sbostic { 8846372Sbostic register u_short n; 8946372Sbostic register u_short off; 9046372Sbostic register u_short *bp = (u_short *) p; 9146372Sbostic 9246372Sbostic /* enter the key first */ 9346372Sbostic n = bp[0]; 9446372Sbostic 9546372Sbostic off = OFFSET(bp) - key->size; 9646372Sbostic bcopy( key->data, p+off, key->size ); 9746372Sbostic bp[++n] = off; 9846372Sbostic 9946372Sbostic /* now the data */ 10046372Sbostic off -= val->size; 10146372Sbostic bcopy(val->data, p + off, val->size); 10246372Sbostic bp[++n] = off; 10346372Sbostic 10446372Sbostic /* adjust page info */ 10546372Sbostic bp[0] = n; 10646372Sbostic bp[n+1] = off - ((n+3)*sizeof(u_short)); 10746372Sbostic bp[n+2] = off; 10846372Sbostic return; 10946372Sbostic } 11046372Sbostic /* 11146372Sbostic 0 OK 11246372Sbostic -1 error 11346372Sbostic */ 11446372Sbostic extern int 11546372Sbostic __delpair(bufp, ndx) 11646372Sbostic BUFHEAD *bufp; 11746372Sbostic register int ndx; 11846372Sbostic { 11946372Sbostic register u_short *bp = (u_short *) bufp->page; 12046372Sbostic register int n = bp[0]; 12146372Sbostic register u_short newoff; 12246372Sbostic u_short pairlen; 12346372Sbostic 12446372Sbostic if ( bp[ndx+1] < REAL_KEY ) return ( __big_delete ( bufp, ndx ) ); 12546372Sbostic if ( ndx != 1 ) newoff = bp[ndx-1]; 12646372Sbostic else newoff = hashp->BSIZE; 12746372Sbostic pairlen = newoff - bp[ndx+1]; 12846372Sbostic 12946372Sbostic if ( ndx != (n-1) ) { 13046372Sbostic /* Hard Case -- need to shuffle keys */ 13146372Sbostic register int i; 13246372Sbostic register char *src = bufp->page + (int)OFFSET(bp); 13346372Sbostic register char *dst = src + (int)pairlen; 13446372Sbostic bcopy ( src, dst, bp[ndx+1] - OFFSET(bp) ); 13546372Sbostic 13646372Sbostic /* Now adjust the pointers */ 13746372Sbostic for ( i = ndx+2; i <= n; i += 2 ) { 13846372Sbostic if ( bp[i+1] == OVFLPAGE ) { 13946372Sbostic bp[i-2] = bp[i]; 14046372Sbostic bp[i-1] = bp[i+1]; 14146372Sbostic } else { 14246372Sbostic bp[i-2] = bp[i] + pairlen; 14346372Sbostic bp[i-1] = bp[i+1] + pairlen; 14446372Sbostic } 14546372Sbostic } 14646372Sbostic } 14746372Sbostic 14846372Sbostic /* Finally adjust the page data */ 14946372Sbostic bp[n] = OFFSET(bp) + pairlen; 15046372Sbostic bp[n-1] = bp[n+1] + pairlen + 2 * sizeof(u_short); 15146372Sbostic bp[0] = n-2; 15246372Sbostic hashp->NKEYS--; 15346372Sbostic 15446372Sbostic bufp->flags |= BUF_MOD; 15546372Sbostic return (0); 15646372Sbostic } 15746372Sbostic /* 15846372Sbostic -1 ==> Error 15946372Sbostic 0 ==> OK 16046372Sbostic */ 16146372Sbostic extern int 16246372Sbostic __split_page(obucket, nbucket) 16346372Sbostic int obucket; 16446372Sbostic int nbucket; 16546372Sbostic { 16646372Sbostic DBT key; 16746372Sbostic DBT val; 16846372Sbostic 16946372Sbostic register BUFHEAD *new_bufp; 17046372Sbostic register BUFHEAD *old_bufp; 17146372Sbostic register u_short *ino; 17246372Sbostic register char *np; 17346460Sbostic int n; 17446460Sbostic int ndx; 17546460Sbostic int retval; 17646372Sbostic char *op; 17746372Sbostic 17846372Sbostic u_short copyto = (u_short)hashp->BSIZE; 17946460Sbostic u_short diff; 18046372Sbostic u_short off = (u_short)hashp->BSIZE; 18146372Sbostic u_short moved; 18246372Sbostic 18346372Sbostic old_bufp = __get_buf ( obucket, NULL, 0 ); 18446372Sbostic new_bufp = __get_buf ( nbucket, NULL, 0 ); 18546372Sbostic 18646460Sbostic old_bufp->flags |= (BUF_MOD|BUF_PIN); 18746460Sbostic new_bufp->flags |= (BUF_MOD|BUF_PIN); 18846372Sbostic 18946372Sbostic ino = (u_short *)(op = old_bufp->page); 19046372Sbostic np = new_bufp->page; 19146372Sbostic 19246372Sbostic moved = 0; 19346372Sbostic 19446372Sbostic for (n = 1, ndx = 1; n < ino[0]; n+=2) { 19546372Sbostic if ( ino[n+1] < REAL_KEY ) { 19646460Sbostic retval = ugly_split( obucket, old_bufp, new_bufp, 19746460Sbostic copyto, moved ); 19846460Sbostic old_bufp->flags &= ~BUF_PIN; 19946460Sbostic new_bufp->flags &= ~BUF_PIN; 20046460Sbostic return(retval); 20146460Sbostic 20246372Sbostic } 20346372Sbostic key.data = op + ino[n]; 20446372Sbostic key.size = off - ino[n]; 20546372Sbostic 20646372Sbostic if ( __call_hash ( key.data, key.size ) == obucket ) { 20746372Sbostic /* Don't switch page */ 20846372Sbostic diff = copyto - off; 20946372Sbostic if ( diff ) { 21046372Sbostic copyto = ino[n+1] + diff; 21146372Sbostic bcopy ( op + ino[n+1], op + copyto, off-ino[n+1]); 21246372Sbostic ino[ndx] = copyto + ino[n] - ino[n+1]; 21346372Sbostic ino[ndx+1] = copyto; 21446372Sbostic } else copyto = ino[n+1]; 21546372Sbostic ndx += 2; 21646372Sbostic } else { 21746372Sbostic /* Switch page */ 21846372Sbostic val.data = op + ino[n+1]; 21946372Sbostic val.size = ino[n] - ino[n+1]; 22046372Sbostic putpair( np, &key, &val); 22146372Sbostic moved +=2; 22246372Sbostic } 22346372Sbostic 22446372Sbostic off = ino[n+1]; 22546372Sbostic } 22646372Sbostic 22746372Sbostic /* Now clean up the page */ 22846372Sbostic ino[0] -= moved; 22946372Sbostic FREESPACE(ino) = copyto - sizeof(u_short) * (ino[0]+3); 23046372Sbostic OFFSET(ino) = copyto; 23146372Sbostic 23246372Sbostic #ifdef DEBUG3 23346372Sbostic fprintf(stderr, "split %d/%d\n", 23446372Sbostic ((u_short *) np)[0] / 2, 23546372Sbostic ((u_short *) op)[0] / 2); 23646372Sbostic #endif 23746460Sbostic /* unpin both pages */ 23846460Sbostic old_bufp->flags &= ~BUF_PIN; 23946460Sbostic new_bufp->flags &= ~BUF_PIN; 24046372Sbostic return(0); 24146372Sbostic } 24246372Sbostic /* 24346372Sbostic 0 ==> success 24446372Sbostic -1 ==> failure 24546372Sbostic 24646460Sbostic Called when we encounter an overflow or big key/data page during 24746460Sbostic split handling. 24846460Sbostic This is special cased since we have to begin checking whether 24946372Sbostic the key/data pairs fit on their respective pages and because 25046372Sbostic we may need overflow pages for both the old and new pages 25146460Sbostic 25246460Sbostic The first page might be a page with regular key/data pairs 25346460Sbostic in which case we have a regular overflow condition and just 25446460Sbostic need to go on to the next page or it might be a big key/data 25546460Sbostic pair in which case we need to fix the big key/data pair. 25646372Sbostic */ 25746372Sbostic static int 25846372Sbostic ugly_split( obucket, old_bufp, new_bufp, copyto, moved ) 25946372Sbostic int obucket; /* Same as __split_page */ 26046372Sbostic BUFHEAD *old_bufp; 26146372Sbostic BUFHEAD *new_bufp; 26246372Sbostic u_short copyto; /* First byte on page which contains key/data values */ 26346372Sbostic int moved; /* number of pairs moved to new page */ 26446372Sbostic { 26546372Sbostic register BUFHEAD *bufp = old_bufp; /* Buffer header for ino */ 26646372Sbostic register u_short *ino = (u_short *)old_bufp->page; 26746372Sbostic /* Page keys come off of */ 26846372Sbostic register u_short *np = (u_short *)new_bufp->page; /* New page */ 26946372Sbostic register u_short *op = (u_short *)old_bufp->page; 27046372Sbostic /* Page keys go on to if they 27146372Sbostic aren't moving */ 27246372Sbostic 27346372Sbostic char *cino; /* Character value of ino */ 27446372Sbostic BUFHEAD *last_bfp = NULL; /* Last buffer header OVFL which 27546372Sbostic needs to be freed */ 27646372Sbostic u_short ov_addr, last_addr = 0; 27746372Sbostic u_short n; 27846372Sbostic u_short off; 27946372Sbostic 28046372Sbostic DBT key, val; 28146372Sbostic SPLIT_RETURN ret; 28246372Sbostic 28346372Sbostic n = ino[0]-1; 28446372Sbostic while ( n < ino[0] ) { 28546372Sbostic if ( ino[n+1] == OVFLPAGE ) { 28646372Sbostic ov_addr = ino[n]; 28746372Sbostic /* 28846372Sbostic Fix up the old page -- the extra 2 are the fields which 28946372Sbostic contained the overflow information 29046372Sbostic */ 29146372Sbostic ino[0] -= (moved + 2); 29246372Sbostic FREESPACE(ino) = copyto - sizeof(u_short) * (ino[0]+3); 29346372Sbostic OFFSET(ino) = copyto; 29446372Sbostic 29546372Sbostic bufp = __get_buf ( ov_addr, bufp, 0 ); 29646372Sbostic if ( !bufp ) return(-1); 29746372Sbostic 29846372Sbostic ino = (u_short *)bufp->page; 29946372Sbostic n = 1; 30046372Sbostic copyto = hashp->BSIZE; 30146372Sbostic moved = 0; 30246372Sbostic 30346372Sbostic if ( last_bfp ) { 30446372Sbostic __free_ovflpage( last_bfp); 30546372Sbostic } 30646372Sbostic last_bfp = bufp; 30746372Sbostic } 30846372Sbostic 30946372Sbostic if ( (ino[2] < REAL_KEY) && (ino[2] != OVFLPAGE) ) { 31046372Sbostic if (__big_split (old_bufp, 31146372Sbostic new_bufp, bufp, ov_addr, obucket, &ret)) { 31246372Sbostic return(-1); 31346372Sbostic } 31446372Sbostic old_bufp = ret.oldp; 31546372Sbostic if ( !old_bufp ) return(-1); 31646372Sbostic op = (u_short *)old_bufp->page; 31746372Sbostic new_bufp = ret.newp; 31846372Sbostic if ( !new_bufp ) return(-1); 31946372Sbostic np = (u_short *)new_bufp->page; 32046372Sbostic bufp = ret.nextp; 32146372Sbostic if ( !bufp ) return(0); 32246372Sbostic cino = (char *)bufp->page; 32346372Sbostic ino = (u_short *)cino; 32446372Sbostic last_bfp = ret.nextp; 32546372Sbostic } 32646372Sbostic 32746372Sbostic 32846460Sbostic /* Move regular sized pairs of there are any */ 32946372Sbostic off = hashp->BSIZE; 33046372Sbostic for ( n = 1; (n < ino[0]) && (ino[n+1] >= REAL_KEY); n += 2 ) { 33146372Sbostic cino = (char *)ino; 33246372Sbostic key.data = cino + ino[n]; 33346372Sbostic key.size = off - ino[n]; 33446372Sbostic val.data = cino + ino[n+1]; 33546372Sbostic val.size = ino[n] - ino[n+1]; 33646372Sbostic off = ino[n+1]; 33746372Sbostic 33846372Sbostic if ( __call_hash ( key.data, key.size ) == obucket ) { 33946372Sbostic /* Keep on old page */ 34046372Sbostic if (PAIRFITS(op,(&key),(&val))) putpair((char *)op, &key, &val); 34146372Sbostic else { 34246372Sbostic old_bufp = __add_ovflpage ( old_bufp ); 34346372Sbostic if ( !old_bufp ) return(-1); 34446372Sbostic op = (u_short *)old_bufp->page; 34546372Sbostic putpair ((char *)op, &key, &val); 34646372Sbostic } 34746372Sbostic old_bufp->flags |= BUF_MOD; 34846372Sbostic } else { 34946372Sbostic /* Move to new page */ 35046372Sbostic if (PAIRFITS(np,(&key),(&val))) putpair((char *)np, &key, &val); 35146372Sbostic else { 35246372Sbostic new_bufp = __add_ovflpage ( new_bufp ); 35346372Sbostic if ( !new_bufp )return(-1); 35446372Sbostic np = (u_short *)new_bufp->page; 35546372Sbostic putpair ((char *)np, &key, &val); 35646372Sbostic } 35746372Sbostic new_bufp->flags |= BUF_MOD; 35846372Sbostic } 35946372Sbostic } 36046372Sbostic } 36146372Sbostic if ( last_bfp ) { 36246372Sbostic __free_ovflpage(last_bfp); 36346372Sbostic } 36446372Sbostic 36546372Sbostic return (0); 36646372Sbostic } 36746372Sbostic /* 36846372Sbostic Add the given pair to the page 36946372Sbostic 1 ==> failure 37046372Sbostic 0 ==> OK 37146372Sbostic */ 37246372Sbostic extern int 37346372Sbostic __addel(bufp, key, val) 37446372Sbostic BUFHEAD *bufp; 37546372Sbostic DBT *key; 37646372Sbostic DBT *val; 37746372Sbostic { 37846372Sbostic register u_short *bp = (u_short *)bufp->page; 37946372Sbostic register u_short *sop; 38046372Sbostic int do_expand; 38146372Sbostic 38246372Sbostic do_expand = 0; 38346372Sbostic while ( bp[0] && (bp[bp[0]] < REAL_KEY) ) { 38446372Sbostic /* Exception case */ 38546372Sbostic if ( bp[2] < REAL_KEY ) { 38646372Sbostic /* This is a big-keydata pair */ 38746372Sbostic bufp = __add_ovflpage(bufp); 38846372Sbostic if ( !bufp ) { 38946372Sbostic return(-1); 39046372Sbostic } 39146372Sbostic bp = (u_short *)bufp->page; 39246372Sbostic } else { 39346372Sbostic /* Try to squeeze key on this page */ 39446372Sbostic if ( FREESPACE(bp) > PAIRSIZE(key,val) ) { 39546372Sbostic squeeze_key ( bp, key, val ); 39646372Sbostic return(0); 39746372Sbostic } else { 39846372Sbostic bufp = __get_buf ( bp[bp[0]-1], bufp, 0 ); 39946372Sbostic if (!bufp) { 40046372Sbostic return(-1); 40146372Sbostic } 40246372Sbostic bp = (u_short *)bufp->page; 40346372Sbostic } 40446372Sbostic } 40546372Sbostic } 40646372Sbostic 40746372Sbostic if ( PAIRFITS(bp,key,val) ) putpair (bufp->page, key, val); 40846372Sbostic else { 40946372Sbostic do_expand = 1; 41046372Sbostic bufp = __add_ovflpage ( bufp ); 41146372Sbostic if (!bufp)return(-1); 41246372Sbostic sop = (u_short *) bufp->page; 41346372Sbostic 41446372Sbostic if ( PAIRFITS(sop, key, val) ) putpair ( (char *)sop, key, val ); 41546372Sbostic else if ( __big_insert ( bufp, key, val ) ) { 41646372Sbostic return(-1); 41746372Sbostic } 41846372Sbostic } 41946372Sbostic bufp->flags |= BUF_MOD; 42046372Sbostic /* 42146372Sbostic If the average number of keys per bucket exceeds the fill factor, 42246372Sbostic expand the table 42346372Sbostic */ 42446372Sbostic hashp->NKEYS++; 42546372Sbostic if (do_expand || 42646372Sbostic (hashp->NKEYS / (hashp->MAX_BUCKET+1) > hashp->FFACTOR) ) { 42746372Sbostic return(__expand_table()); 42846372Sbostic } 42946372Sbostic return(0); 43046372Sbostic } 43146372Sbostic 43246372Sbostic /* 43346372Sbostic returns a pointer, NULL on error 43446372Sbostic */ 43546372Sbostic extern BUFHEAD * 43646372Sbostic __add_ovflpage ( bufp ) 43746372Sbostic BUFHEAD *bufp; 43846372Sbostic { 43946372Sbostic register u_short *sp = (u_short *)bufp->page; 44046372Sbostic 44146372Sbostic u_short ovfl_num; 44246372Sbostic u_short ndx, newoff; 44346372Sbostic char *op; 44446372Sbostic DBT okey, oval; 44546372Sbostic #ifdef DEBUG1 44646372Sbostic int tmp1, tmp2; 44746372Sbostic #endif 44846372Sbostic 44946372Sbostic bufp->flags |= BUF_MOD; 45046372Sbostic ovfl_num = overflow_page (); 45146372Sbostic #ifdef DEBUG1 45246372Sbostic tmp1 = bufp->addr; 45346372Sbostic tmp2 = bufp->ovfl?bufp->ovfl->addr:0; 45446372Sbostic #endif 45546372Sbostic if (!ovfl_num || !(bufp->ovfl = __get_buf ( ovfl_num, bufp, 1 ))) { 45646372Sbostic return(NULL); 45746372Sbostic } 45846372Sbostic bufp->ovfl->flags |= BUF_MOD; 45946372Sbostic #ifdef DEBUG1 46046372Sbostic fprintf ( stderr, "ADDOVFLPAGE: %d->ovfl was %d is now %d\n", tmp1, tmp2, 46146372Sbostic bufp->ovfl->addr ); 46246372Sbostic #endif 46346372Sbostic ndx = sp[0]; 46446372Sbostic /* 46546372Sbostic Since a pair is allocated on a page only if there's room 46646372Sbostic to add an overflow page, we know that the OVFL information 46746372Sbostic will fit on the page 46846372Sbostic */ 46946372Sbostic sp[ndx+4] = OFFSET(sp); 47046372Sbostic sp[ndx+3] = FREESPACE(sp) - OVFLSIZE; 47146372Sbostic sp[ndx+1] = ovfl_num; 47246372Sbostic sp[ndx+2] = OVFLPAGE; 47346372Sbostic sp[0] = ndx+2; 47446372Sbostic #ifdef HASH_STATISTICS 47546372Sbostic hash_overflows++; 47646372Sbostic #endif 47746372Sbostic return(bufp->ovfl); 47846372Sbostic } 47946372Sbostic 48046372Sbostic /* 48146372Sbostic 0 indicates SUCCESS 48246372Sbostic -1 indicates FAILURE 48346372Sbostic */ 48446372Sbostic extern int 48546372Sbostic __get_page ( p, bucket, is_bucket, is_disk, is_bitmap ) 48646372Sbostic char *p; 48746372Sbostic int bucket; 48846372Sbostic int is_bucket; 48946372Sbostic int is_disk; 49046372Sbostic int is_bitmap; 49146372Sbostic { 49246372Sbostic register int size; 49346372Sbostic register int fd; 49446372Sbostic register int page; 49546372Sbostic u_short *bp; 49646372Sbostic int rsize; 49746372Sbostic 49846372Sbostic fd = hashp->fp; 49946372Sbostic size = hashp->BSIZE; 50046372Sbostic 50146460Sbostic if ( (fd == -1) || (hashp->new_file && !is_disk) ) { 50246372Sbostic PAGE_INIT(p); 50346372Sbostic return(0); 50446372Sbostic } 50546372Sbostic 50646372Sbostic if ( is_bucket) page = BUCKET_TO_PAGE (bucket); 50746372Sbostic else page = OADDR_TO_PAGE (bucket); 50846372Sbostic if ((lseek ( fd, page << hashp->BSHIFT, L_SET ) == -1) || 50946372Sbostic ((rsize = read ( fd, p, size )) == -1 )) { 51046372Sbostic return(-1); 51146372Sbostic } 51246372Sbostic bp = (u_short *)p; 51346372Sbostic if ( !rsize ) { 51446372Sbostic bp[0] = 0; /* We hit the EOF, so initialize a new page */ 51546372Sbostic } else if ( rsize != size ) { 51646372Sbostic errno = EFTYPE; 51746372Sbostic return(-1); 51846372Sbostic } 51946372Sbostic if (!bp[0]) { 52046372Sbostic PAGE_INIT(p); 52146372Sbostic } else if ( hashp->LORDER != BYTE_ORDER ) { 52246372Sbostic register int i; 52346372Sbostic register int max; 52446372Sbostic 52546372Sbostic if ( is_bitmap ) { 52646372Sbostic max = hashp->BSIZE >> 2; /* divide by 4 */ 52746372Sbostic for ( i=0; i < max; i++ ) { 52846372Sbostic BLSWAP(((long *)p)[i]); 52946372Sbostic } 53046372Sbostic } else { 53146372Sbostic BSSWAP(bp[0]); 53246372Sbostic max = bp[0] + 2; 53346372Sbostic for ( i=1; i <= max; i++ ) { 53446372Sbostic BSSWAP(bp[i]); 53546372Sbostic } 53646372Sbostic } 53746372Sbostic } 53846372Sbostic return (0); 53946372Sbostic } 54046372Sbostic 54146372Sbostic /* 54246372Sbostic Write page p to disk 54346372Sbostic -1==>failure 54446372Sbostic 0==> OK 54546372Sbostic */ 54646372Sbostic extern int 54746372Sbostic __put_page ( p, bucket, is_bucket, is_bitmap ) 54846372Sbostic char *p; 54946372Sbostic int bucket; 55046372Sbostic int is_bucket; 55146372Sbostic int is_bitmap; 55246372Sbostic { 55346372Sbostic register int size; 55446372Sbostic register int fd; 55546372Sbostic register int page; 55646372Sbostic int wsize; 55746372Sbostic 55846372Sbostic size = hashp->BSIZE; 55946460Sbostic if ( (hashp->fp == -1) && open_temp() ) return (1); 56046372Sbostic fd = hashp->fp; 56146372Sbostic 56246372Sbostic if ( hashp->LORDER != BYTE_ORDER ) { 56346372Sbostic register int i; 56446372Sbostic register int max; 56546372Sbostic 56646372Sbostic if ( is_bitmap ) { 56746372Sbostic max = hashp->BSIZE >> 2; /* divide by 4 */ 56846372Sbostic for ( i=0; i < max; i++ ) { 56946372Sbostic BLSWAP(((long *)p)[i]); 57046372Sbostic } 57146372Sbostic } else { 57246372Sbostic max = ((u_short *)p)[0] + 2; 57346372Sbostic for ( i=0; i <= max; i++ ) { 57446372Sbostic BSSWAP(((u_short *)p)[i]); 57546372Sbostic } 57646372Sbostic } 57746372Sbostic } 57846372Sbostic if (is_bucket ) page = BUCKET_TO_PAGE (bucket); 57946372Sbostic else page = OADDR_TO_PAGE ( bucket ); 58046372Sbostic if ((lseek ( fd, page << hashp->BSHIFT, L_SET ) == -1) || 58146372Sbostic ((wsize = write ( fd, p, size )) == -1 )) { 58246372Sbostic /* Errno is set */ 58346372Sbostic return(-1); 58446372Sbostic } 58546372Sbostic if ( wsize != size ) { 58646372Sbostic errno = EFTYPE; 58746372Sbostic return(-1); 58846372Sbostic } 58946372Sbostic return(0); 59046372Sbostic } 59146372Sbostic #define BYTE_MASK ((1 << INT_BYTE_SHIFT) -1) 59246372Sbostic /* 59346372Sbostic Initialize a new bitmap page. Bitmap pages are left in memory 59446372Sbostic once they are read in. 59546372Sbostic */ 59646372Sbostic extern u_long * 59746372Sbostic __init_bitmap(pnum, nbits, ndx) 59846372Sbostic u_short pnum; 59946372Sbostic int nbits; 60046372Sbostic int ndx; 60146372Sbostic { 60246372Sbostic u_long *ip; 60346372Sbostic int clearints; 60446372Sbostic int clearbytes; 60546372Sbostic 60646372Sbostic if ( !(ip = (u_long *)malloc (hashp->BSIZE)) ) return (NULL); 60746372Sbostic clearints = ((nbits - 1) >> INT_BYTE_SHIFT) + 1; 60846372Sbostic clearbytes = clearints << INT_TO_BYTE; 60946372Sbostic memset ((char *)ip, 0, clearbytes ); 61046372Sbostic memset ( ((char *) ip) + clearbytes, 0xFF, 61146372Sbostic hashp->BSIZE-clearbytes ); 61246372Sbostic ip[clearints-1] = ALL_SET << (nbits & BYTE_MASK); 61346372Sbostic SETBIT(ip, 0); 61446372Sbostic hashp->BITMAPS[ndx] = pnum; 61546372Sbostic hashp->mapp[ndx] = ip; 61646372Sbostic return(ip); 61746372Sbostic } 61846372Sbostic static int 61946372Sbostic first_free ( map ) 62046372Sbostic u_long map; 62146372Sbostic { 62246372Sbostic register u_long mask; 62346372Sbostic register u_long i; 62446372Sbostic 62546372Sbostic mask = 0x1; 62646372Sbostic for ( i=0; i < BITS_PER_MAP; i++ ) { 62746372Sbostic if ( !(mask & map) ) return(i); 62846372Sbostic mask = mask << 1; 62946372Sbostic } 63046372Sbostic return ( i ); 63146372Sbostic } 63246372Sbostic 63346372Sbostic static u_short 63446372Sbostic overflow_page ( ) 63546372Sbostic { 63646372Sbostic register int max_free; 63746372Sbostic register int splitnum; 63846372Sbostic register u_long *freep; 63946372Sbostic register int offset; 64046372Sbostic u_short addr; 64146372Sbostic int in_use_bits; 64246372Sbostic int free_page, free_bit; 64346372Sbostic int i, j, bit; 64446372Sbostic #ifdef DEBUG2 64546372Sbostic int tmp1, tmp2; 64646372Sbostic #endif 64746372Sbostic 64846372Sbostic splitnum = __log2(hashp->MAX_BUCKET); 64946372Sbostic max_free = hashp->SPARES[splitnum]; 65046372Sbostic 65146372Sbostic free_page = (max_free-1) >> (hashp->BSHIFT + BYTE_SHIFT); 65246372Sbostic free_bit = (max_free-1) & ((hashp->BSIZE << BYTE_SHIFT) - 1); 65346372Sbostic 65446372Sbostic /* Look through all the free maps to find the first free block */ 65546372Sbostic for ( i = 0; i <= free_page; i++ ) { 65646372Sbostic if (!(freep = (u_long *)hashp->mapp[i]) ) return(NULL); 65746372Sbostic if ( i == free_page ) in_use_bits = free_bit; 65846372Sbostic else in_use_bits = (hashp->BSIZE << BYTE_SHIFT) -1; 65946372Sbostic 66046372Sbostic for (j = 0, bit = 0; bit <= in_use_bits; j++, bit += BITS_PER_MAP ) { 66146372Sbostic if ( freep[j] != ALL_SET ) goto found; 66246372Sbostic } 66346372Sbostic } 66446372Sbostic /* No Free Page Found */ 66546372Sbostic hashp->SPARES[splitnum]++; 66646372Sbostic offset = hashp->SPARES[splitnum] - 66746372Sbostic (splitnum ? hashp->SPARES[splitnum-1] : 0); 66846372Sbostic 66946372Sbostic /* Check if we need to allocate a new bitmap page */ 67046372Sbostic if ( free_bit == (hashp->BSIZE << BYTE_SHIFT) - 1 ) { 67146372Sbostic free_page++; 67246372Sbostic if ( free_page >= NCACHED ) { 67346372Sbostic fprintf ( stderr, 67446372Sbostic "HASH: Out of overflow pages. Increase page size\n" ); 67546372Sbostic return(NULL); 67646372Sbostic } 67746372Sbostic /* 67846372Sbostic This is tricky. The 1 indicates that you want the 67946372Sbostic new page allocated with 1 clear bit. Actually, you 68046372Sbostic are going to allocate 2 pages from this map. The first 68146372Sbostic is going to be the map page, the second is the overflow 68246372Sbostic page we were looking for. The init_bitmap routine 68346372Sbostic automatically, sets the first bit of itself to indicate 68446372Sbostic that the bitmap itself is in use. We would explicitly 68546372Sbostic set the second bit, but don't have to if we tell init_bitmap 68646372Sbostic not to leave it clear in the first place. 68746372Sbostic */ 68846372Sbostic __init_bitmap ( OADDR_OF(splitnum, offset), 1, free_page ); 68946372Sbostic hashp->SPARES[splitnum]++; 69046372Sbostic #ifdef DEBUG2 69146372Sbostic free_bit = 2; 69246372Sbostic #endif 69346372Sbostic offset++; 69446372Sbostic } else { 69546372Sbostic /* 69646372Sbostic Free_bit addresses the last used bit. Bump it to 69746372Sbostic address the first available bit. 69846372Sbostic */ 69946372Sbostic free_bit++; 70046372Sbostic SETBIT ( freep, free_bit ); 70146372Sbostic } 70246372Sbostic 70346372Sbostic /* Calculate address of the new overflow page */ 70446372Sbostic if ( offset > SPLITMASK ) { 70546372Sbostic fprintf ( stderr, 70646372Sbostic "HASH: Out of overflow pages. Increase page size\n" ); 70746372Sbostic return(NULL); 70846372Sbostic } 70946372Sbostic addr = OADDR_OF(splitnum, offset); 71046372Sbostic #ifdef DEBUG2 71146372Sbostic fprintf ( stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", 71246372Sbostic addr, free_bit, free_page ); 71346372Sbostic #endif 71446372Sbostic return(addr); 71546372Sbostic 71646372Sbostic found: 71746372Sbostic bit = bit + first_free(freep[j]); 71846372Sbostic SETBIT(freep,bit); 71946372Sbostic #ifdef DEBUG2 72046372Sbostic tmp1 = bit; 72146372Sbostic tmp2 = i; 72246372Sbostic #endif 72346372Sbostic /* 72446372Sbostic Bits are addressed starting with 0, but overflow pages are 72546372Sbostic addressed beginning at 1. Bit is a bit addressnumber, so we 72646372Sbostic need to increment it to convert it to a page number. 72746372Sbostic */ 72846372Sbostic bit = 1 + bit + (i * (hashp->BSIZE << BYTE_SHIFT)); 72946372Sbostic 73046372Sbostic /* Calculate the split number for this page */ 73146372Sbostic for ( i = 0; (i < splitnum) && (bit > hashp->SPARES[i]); i++ ); 73246372Sbostic offset =(i ? bit - hashp->SPARES[i-1] : bit ); 73346372Sbostic if ( offset >= SPLITMASK ) return(NULL);/* Out of overflow pages */ 73446372Sbostic addr = OADDR_OF(i, offset); 73546372Sbostic #ifdef DEBUG2 73646372Sbostic fprintf ( stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", 73746372Sbostic addr, tmp1, tmp2 ); 73846372Sbostic #endif 73946372Sbostic 74046372Sbostic /* Allocate and return the overflow page */ 74146372Sbostic return (addr); 74246372Sbostic } 74346372Sbostic 74446372Sbostic /* 74546372Sbostic Mark this overflow page as free. 74646372Sbostic */ 74746372Sbostic __free_ovflpage ( obufp ) 74846372Sbostic BUFHEAD *obufp; 74946372Sbostic { 75046372Sbostic register u_short addr = obufp->addr; 75146372Sbostic int free_page, free_bit; 75246372Sbostic int bit_address; 75346372Sbostic u_short ndx; 75446372Sbostic u_long *freep; 75546372Sbostic int j; 75646372Sbostic 75746372Sbostic #ifdef DEBUG1 75846372Sbostic fprintf ( stderr, "Freeing %d\n", addr ); 75946372Sbostic #endif 76046372Sbostic ndx = (((u_short)addr) >> SPLITSHIFT); 76146372Sbostic bit_address = (ndx ? hashp->SPARES[ndx-1] : 0) + (addr & SPLITMASK) - 1; 76246372Sbostic free_page = (bit_address >> (hashp->BSHIFT + BYTE_SHIFT)); 76346372Sbostic free_bit = bit_address & ((hashp->BSIZE << BYTE_SHIFT) - 1); 76446372Sbostic 76546372Sbostic freep = hashp->mapp[free_page]; 76646372Sbostic assert(freep); 76746372Sbostic CLRBIT(freep, free_bit); 76846372Sbostic #ifdef DEBUG2 76946372Sbostic fprintf ( stderr, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n", 77046372Sbostic obufp->addr, free_bit, free_page ); 77146372Sbostic #endif 77246372Sbostic __reclaim_buf ( obufp ); 77346372Sbostic return; 77446372Sbostic } 77546372Sbostic 77646372Sbostic /* 77746372Sbostic 0 success 77846372Sbostic -1 failure 77946372Sbostic */ 78046372Sbostic static int 78146372Sbostic open_temp() 78246372Sbostic { 78346416Sbostic sigset_t set, oset; 784*46499Sbostic char namestr[] = "_hashXXXXXX"; 78546372Sbostic 78646416Sbostic /* Block signals; make sure file goes away at process exit. */ 78746416Sbostic sigemptyset(&set); 78846416Sbostic sigaddset(&set, SIGHUP); 78946416Sbostic sigaddset(&set, SIGINT); 79046416Sbostic sigaddset(&set, SIGQUIT); 79146416Sbostic sigaddset(&set, SIGTERM); 79246416Sbostic (void)sigprocmask(SIG_BLOCK, &set, &oset); 79346416Sbostic if ((hashp->fp = mkstemp ( namestr )) != -1) { 79446416Sbostic (void)unlink(namestr); 79546416Sbostic (void)fcntl(hashp->fp, F_SETFD, 1); 79646372Sbostic } 79746416Sbostic (void)sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL); 79846416Sbostic return(hashp->fp != -1 ? 0 : -1); 79946372Sbostic } 80046372Sbostic 80146372Sbostic /* 80246372Sbostic We have to know that the key will fit, but the 80346372Sbostic last entry on the page is an overflow pair, so we 80446372Sbostic need to shift things. 80546372Sbostic */ 80646372Sbostic static void 80746372Sbostic squeeze_key ( sp, key, val ) 80846372Sbostic u_short *sp; 80946372Sbostic DBT *key; 81046372Sbostic DBT *val; 81146372Sbostic { 81246372Sbostic register char *p = (char *)sp; 81346372Sbostic u_short free_space, off; 81446372Sbostic u_short pageno, n; 81546372Sbostic 81646372Sbostic n = sp[0]; 81746372Sbostic free_space = FREESPACE(sp); 81846372Sbostic off = OFFSET(sp); 81946372Sbostic 82046372Sbostic pageno = sp[n-1]; 82146372Sbostic off -= key->size; 82246372Sbostic sp[n-1] = off; 82346372Sbostic bcopy ( key->data, p + off, key->size ); 82446372Sbostic off -= val->size; 82546372Sbostic sp[n] = off; 82646372Sbostic bcopy ( val->data, p + off, val->size ); 82746372Sbostic sp[0] = n+2; 82846372Sbostic sp[n+1] = pageno; 82946372Sbostic sp[n+2] = OVFLPAGE; 83046372Sbostic FREESPACE(sp) = free_space - PAIRSIZE(key,val); 83146372Sbostic OFFSET(sp) = off; 83246372Sbostic } 83346372Sbostic 83446372Sbostic #ifdef DEBUG4 83546372Sbostic print_chain ( addr ) 83646372Sbostic short addr; 83746372Sbostic { 83846372Sbostic BUFHEAD *bufp; 83946372Sbostic short *bp; 84046372Sbostic short oaddr; 84146372Sbostic 84246372Sbostic fprintf ( stderr, "%d ", addr ); 84346372Sbostic bufp = __get_buf ( (int)addr, NULL, 0 ); 84446372Sbostic bp = (short *)bufp->page; 84546372Sbostic while ( bp[0] && 84646372Sbostic ((bp[bp[0]] == OVFLPAGE) || 84746372Sbostic ((bp[0] > 2) && bp[2] < REAL_KEY))) { 84846372Sbostic oaddr = bp[bp[0]-1]; 84946372Sbostic fprintf ( stderr, "%d ", (int)oaddr ); 85046372Sbostic bufp = __get_buf ( (int)oaddr, bufp, 0 ); 85146372Sbostic bp = (short *)bufp->page; 85246372Sbostic } 85346372Sbostic fprintf ( stderr, "\n" ); 85446372Sbostic } 85546372Sbostic #endif 856