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*46505Sbostic static char sccsid[] = "@(#)hash_page.c 5.7 (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] ) { 28646502Sbostic if ( ino[2] < REAL_KEY && ino[2] != OVFLPAGE ) { 28746502Sbostic if (__big_split (old_bufp, new_bufp, bufp, ov_addr, obucket, &ret)) { 28846502Sbostic return(-1); 28946502Sbostic } 29046502Sbostic old_bufp = ret.oldp; 29146502Sbostic if ( !old_bufp ) return(-1); 29246502Sbostic op = (u_short *)old_bufp->page; 29346502Sbostic new_bufp = ret.newp; 29446502Sbostic if ( !new_bufp ) return(-1); 29546502Sbostic np = (u_short *)new_bufp->page; 29646502Sbostic bufp = ret.nextp; 29746502Sbostic if ( !bufp ) return(0); 29846502Sbostic cino = (char *)bufp->page; 29946502Sbostic ino = (u_short *)cino; 30046502Sbostic last_bfp = ret.nextp; 30146502Sbostic } 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 49946502Sbostic 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); 605*46505Sbostic hashp->exmaps++; 60646372Sbostic clearints = ((nbits - 1) >> INT_BYTE_SHIFT) + 1; 60746372Sbostic clearbytes = clearints << INT_TO_BYTE; 60846372Sbostic memset ((char *)ip, 0, clearbytes ); 60946372Sbostic memset ( ((char *) ip) + clearbytes, 0xFF, 61046372Sbostic hashp->BSIZE-clearbytes ); 61146372Sbostic ip[clearints-1] = ALL_SET << (nbits & BYTE_MASK); 61246372Sbostic SETBIT(ip, 0); 61346372Sbostic hashp->BITMAPS[ndx] = pnum; 61446372Sbostic hashp->mapp[ndx] = ip; 61546372Sbostic return(ip); 61646372Sbostic } 61746372Sbostic static int 61846372Sbostic first_free ( map ) 61946372Sbostic u_long map; 62046372Sbostic { 62146372Sbostic register u_long mask; 62246372Sbostic register u_long i; 62346372Sbostic 62446372Sbostic mask = 0x1; 62546372Sbostic for ( i=0; i < BITS_PER_MAP; i++ ) { 62646372Sbostic if ( !(mask & map) ) return(i); 62746372Sbostic mask = mask << 1; 62846372Sbostic } 62946372Sbostic return ( i ); 63046372Sbostic } 63146372Sbostic 63246372Sbostic static u_short 63346372Sbostic overflow_page ( ) 63446372Sbostic { 63546372Sbostic register int max_free; 63646372Sbostic register int splitnum; 63746372Sbostic register u_long *freep; 63846372Sbostic register int offset; 63946372Sbostic u_short addr; 64046372Sbostic int in_use_bits; 64146372Sbostic int free_page, free_bit; 64246372Sbostic int i, j, bit; 64346372Sbostic #ifdef DEBUG2 64446372Sbostic int tmp1, tmp2; 64546372Sbostic #endif 64646372Sbostic 64746372Sbostic splitnum = __log2(hashp->MAX_BUCKET); 64846372Sbostic max_free = hashp->SPARES[splitnum]; 64946372Sbostic 65046372Sbostic free_page = (max_free-1) >> (hashp->BSHIFT + BYTE_SHIFT); 65146372Sbostic free_bit = (max_free-1) & ((hashp->BSIZE << BYTE_SHIFT) - 1); 65246372Sbostic 65346372Sbostic /* Look through all the free maps to find the first free block */ 65446372Sbostic for ( i = 0; i <= free_page; i++ ) { 65546372Sbostic if (!(freep = (u_long *)hashp->mapp[i]) ) return(NULL); 65646372Sbostic if ( i == free_page ) in_use_bits = free_bit; 65746372Sbostic else in_use_bits = (hashp->BSIZE << BYTE_SHIFT) -1; 65846372Sbostic 65946372Sbostic for (j = 0, bit = 0; bit <= in_use_bits; j++, bit += BITS_PER_MAP ) { 66046372Sbostic if ( freep[j] != ALL_SET ) goto found; 66146372Sbostic } 66246372Sbostic } 66346372Sbostic /* No Free Page Found */ 66446372Sbostic hashp->SPARES[splitnum]++; 66546372Sbostic offset = hashp->SPARES[splitnum] - 66646372Sbostic (splitnum ? hashp->SPARES[splitnum-1] : 0); 66746372Sbostic 66846372Sbostic /* Check if we need to allocate a new bitmap page */ 66946372Sbostic if ( free_bit == (hashp->BSIZE << BYTE_SHIFT) - 1 ) { 67046372Sbostic free_page++; 67146372Sbostic if ( free_page >= NCACHED ) { 67246372Sbostic fprintf ( stderr, 67346372Sbostic "HASH: Out of overflow pages. Increase page size\n" ); 67446372Sbostic return(NULL); 67546372Sbostic } 67646372Sbostic /* 67746372Sbostic This is tricky. The 1 indicates that you want the 67846372Sbostic new page allocated with 1 clear bit. Actually, you 67946372Sbostic are going to allocate 2 pages from this map. The first 68046372Sbostic is going to be the map page, the second is the overflow 68146372Sbostic page we were looking for. The init_bitmap routine 68246372Sbostic automatically, sets the first bit of itself to indicate 68346372Sbostic that the bitmap itself is in use. We would explicitly 68446372Sbostic set the second bit, but don't have to if we tell init_bitmap 68546372Sbostic not to leave it clear in the first place. 68646372Sbostic */ 68746372Sbostic __init_bitmap ( OADDR_OF(splitnum, offset), 1, free_page ); 68846372Sbostic hashp->SPARES[splitnum]++; 68946372Sbostic #ifdef DEBUG2 69046372Sbostic free_bit = 2; 69146372Sbostic #endif 69246372Sbostic offset++; 69346372Sbostic } else { 69446372Sbostic /* 69546372Sbostic Free_bit addresses the last used bit. Bump it to 69646372Sbostic address the first available bit. 69746372Sbostic */ 69846372Sbostic free_bit++; 69946372Sbostic SETBIT ( freep, free_bit ); 70046372Sbostic } 70146372Sbostic 70246372Sbostic /* Calculate address of the new overflow page */ 70346372Sbostic if ( offset > SPLITMASK ) { 70446372Sbostic fprintf ( stderr, 70546372Sbostic "HASH: Out of overflow pages. Increase page size\n" ); 70646372Sbostic return(NULL); 70746372Sbostic } 70846372Sbostic addr = OADDR_OF(splitnum, offset); 70946372Sbostic #ifdef DEBUG2 71046372Sbostic fprintf ( stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", 71146372Sbostic addr, free_bit, free_page ); 71246372Sbostic #endif 71346372Sbostic return(addr); 71446372Sbostic 71546372Sbostic found: 71646372Sbostic bit = bit + first_free(freep[j]); 71746372Sbostic SETBIT(freep,bit); 71846372Sbostic #ifdef DEBUG2 71946372Sbostic tmp1 = bit; 72046372Sbostic tmp2 = i; 72146372Sbostic #endif 72246372Sbostic /* 72346372Sbostic Bits are addressed starting with 0, but overflow pages are 72446372Sbostic addressed beginning at 1. Bit is a bit addressnumber, so we 72546372Sbostic need to increment it to convert it to a page number. 72646372Sbostic */ 72746372Sbostic bit = 1 + bit + (i * (hashp->BSIZE << BYTE_SHIFT)); 72846372Sbostic 72946372Sbostic /* Calculate the split number for this page */ 73046372Sbostic for ( i = 0; (i < splitnum) && (bit > hashp->SPARES[i]); i++ ); 73146372Sbostic offset =(i ? bit - hashp->SPARES[i-1] : bit ); 73246372Sbostic if ( offset >= SPLITMASK ) return(NULL);/* Out of overflow pages */ 73346372Sbostic addr = OADDR_OF(i, offset); 73446372Sbostic #ifdef DEBUG2 73546372Sbostic fprintf ( stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", 73646372Sbostic addr, tmp1, tmp2 ); 73746372Sbostic #endif 73846372Sbostic 73946372Sbostic /* Allocate and return the overflow page */ 74046372Sbostic return (addr); 74146372Sbostic } 74246372Sbostic 74346372Sbostic /* 74446372Sbostic Mark this overflow page as free. 74546372Sbostic */ 74646372Sbostic __free_ovflpage ( obufp ) 74746372Sbostic BUFHEAD *obufp; 74846372Sbostic { 74946372Sbostic register u_short addr = obufp->addr; 75046372Sbostic int free_page, free_bit; 75146372Sbostic int bit_address; 75246372Sbostic u_short ndx; 75346372Sbostic u_long *freep; 75446372Sbostic int j; 75546372Sbostic 75646372Sbostic #ifdef DEBUG1 75746372Sbostic fprintf ( stderr, "Freeing %d\n", addr ); 75846372Sbostic #endif 75946372Sbostic ndx = (((u_short)addr) >> SPLITSHIFT); 76046372Sbostic bit_address = (ndx ? hashp->SPARES[ndx-1] : 0) + (addr & SPLITMASK) - 1; 76146372Sbostic free_page = (bit_address >> (hashp->BSHIFT + BYTE_SHIFT)); 76246372Sbostic free_bit = bit_address & ((hashp->BSIZE << BYTE_SHIFT) - 1); 76346372Sbostic 76446372Sbostic freep = hashp->mapp[free_page]; 76546372Sbostic assert(freep); 76646372Sbostic CLRBIT(freep, free_bit); 76746372Sbostic #ifdef DEBUG2 76846372Sbostic fprintf ( stderr, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n", 76946372Sbostic obufp->addr, free_bit, free_page ); 77046372Sbostic #endif 77146372Sbostic __reclaim_buf ( obufp ); 77246372Sbostic return; 77346372Sbostic } 77446372Sbostic 77546372Sbostic /* 77646372Sbostic 0 success 77746372Sbostic -1 failure 77846372Sbostic */ 77946372Sbostic static int 78046372Sbostic open_temp() 78146372Sbostic { 78246416Sbostic sigset_t set, oset; 78346499Sbostic char namestr[] = "_hashXXXXXX"; 78446372Sbostic 78546416Sbostic /* Block signals; make sure file goes away at process exit. */ 78646416Sbostic sigemptyset(&set); 78746416Sbostic sigaddset(&set, SIGHUP); 78846416Sbostic sigaddset(&set, SIGINT); 78946416Sbostic sigaddset(&set, SIGQUIT); 79046416Sbostic sigaddset(&set, SIGTERM); 79146416Sbostic (void)sigprocmask(SIG_BLOCK, &set, &oset); 79246416Sbostic if ((hashp->fp = mkstemp ( namestr )) != -1) { 79346416Sbostic (void)unlink(namestr); 79446416Sbostic (void)fcntl(hashp->fp, F_SETFD, 1); 79546372Sbostic } 79646416Sbostic (void)sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL); 79746416Sbostic return(hashp->fp != -1 ? 0 : -1); 79846372Sbostic } 79946372Sbostic 80046372Sbostic /* 80146372Sbostic We have to know that the key will fit, but the 80246372Sbostic last entry on the page is an overflow pair, so we 80346372Sbostic need to shift things. 80446372Sbostic */ 80546372Sbostic static void 80646372Sbostic squeeze_key ( sp, key, val ) 80746372Sbostic u_short *sp; 80846372Sbostic DBT *key; 80946372Sbostic DBT *val; 81046372Sbostic { 81146372Sbostic register char *p = (char *)sp; 81246372Sbostic u_short free_space, off; 81346372Sbostic u_short pageno, n; 81446372Sbostic 81546372Sbostic n = sp[0]; 81646372Sbostic free_space = FREESPACE(sp); 81746372Sbostic off = OFFSET(sp); 81846372Sbostic 81946372Sbostic pageno = sp[n-1]; 82046372Sbostic off -= key->size; 82146372Sbostic sp[n-1] = off; 82246372Sbostic bcopy ( key->data, p + off, key->size ); 82346372Sbostic off -= val->size; 82446372Sbostic sp[n] = off; 82546372Sbostic bcopy ( val->data, p + off, val->size ); 82646372Sbostic sp[0] = n+2; 82746372Sbostic sp[n+1] = pageno; 82846372Sbostic sp[n+2] = OVFLPAGE; 82946372Sbostic FREESPACE(sp) = free_space - PAIRSIZE(key,val); 83046372Sbostic OFFSET(sp) = off; 83146372Sbostic } 83246372Sbostic 83346372Sbostic #ifdef DEBUG4 83446372Sbostic print_chain ( addr ) 83546372Sbostic short addr; 83646372Sbostic { 83746372Sbostic BUFHEAD *bufp; 83846372Sbostic short *bp; 83946372Sbostic short oaddr; 84046372Sbostic 84146372Sbostic fprintf ( stderr, "%d ", addr ); 84246372Sbostic bufp = __get_buf ( (int)addr, NULL, 0 ); 84346372Sbostic bp = (short *)bufp->page; 84446372Sbostic while ( bp[0] && 84546372Sbostic ((bp[bp[0]] == OVFLPAGE) || 84646372Sbostic ((bp[0] > 2) && bp[2] < REAL_KEY))) { 84746372Sbostic oaddr = bp[bp[0]-1]; 84846372Sbostic fprintf ( stderr, "%d ", (int)oaddr ); 84946372Sbostic bufp = __get_buf ( (int)oaddr, bufp, 0 ); 85046372Sbostic bp = (short *)bufp->page; 85146372Sbostic } 85246372Sbostic fprintf ( stderr, "\n" ); 85346372Sbostic } 85446372Sbostic #endif 855