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