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*46535Sbostic static char sccsid[] = "@(#)hash_page.c 5.8 (Berkeley) 02/22/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(); 65*46535Sbostic static int ugly_split(); 6646372Sbostic static void squeeze_key(); 6746372Sbostic static void putpair(); 6846372Sbostic 6946372Sbostic #ifdef HASH_STATISTICS 7046372Sbostic extern long hash_accesses, hash_collisions, hash_expansions, hash_overflows; 7146372Sbostic #endif 7246372Sbostic #define PAGE_INIT(P) \ 7346372Sbostic { \ 7446372Sbostic ((u_short *)P)[0] = 0; \ 7546372Sbostic ((u_short *)P)[1] = hashp->BSIZE - 3 * sizeof(u_short); \ 7646372Sbostic ((u_short *)P)[2] = hashp->BSIZE; \ 7746372Sbostic } 7846372Sbostic 7946372Sbostic /* 8046372Sbostic This is called AFTER we have verified that there is room on the 8146372Sbostic page for the pair (PAIRFITS has returned true) so we go right 8246372Sbostic ahead and start moving stuff on. 8346372Sbostic */ 8446372Sbostic static void 8546372Sbostic putpair(p, key, val) 8646372Sbostic char *p; 8746372Sbostic DBT *key; 8846372Sbostic DBT *val; 8946372Sbostic { 9046372Sbostic register u_short n; 9146372Sbostic register u_short off; 9246372Sbostic register u_short *bp = (u_short *) p; 9346372Sbostic 9446372Sbostic /* enter the key first */ 9546372Sbostic n = bp[0]; 9646372Sbostic 9746372Sbostic off = OFFSET(bp) - key->size; 9846372Sbostic bcopy( key->data, p+off, key->size ); 9946372Sbostic bp[++n] = off; 10046372Sbostic 10146372Sbostic /* now the data */ 10246372Sbostic off -= val->size; 10346372Sbostic bcopy(val->data, p + off, val->size); 10446372Sbostic bp[++n] = off; 10546372Sbostic 10646372Sbostic /* adjust page info */ 10746372Sbostic bp[0] = n; 10846372Sbostic bp[n+1] = off - ((n+3)*sizeof(u_short)); 10946372Sbostic bp[n+2] = off; 11046372Sbostic return; 11146372Sbostic } 11246372Sbostic /* 11346372Sbostic 0 OK 11446372Sbostic -1 error 11546372Sbostic */ 11646372Sbostic extern int 11746372Sbostic __delpair(bufp, ndx) 11846372Sbostic BUFHEAD *bufp; 11946372Sbostic register int ndx; 12046372Sbostic { 12146372Sbostic register u_short *bp = (u_short *) bufp->page; 12246372Sbostic register int n = bp[0]; 12346372Sbostic register u_short newoff; 12446372Sbostic u_short pairlen; 12546372Sbostic 12646372Sbostic if ( bp[ndx+1] < REAL_KEY ) return ( __big_delete ( bufp, ndx ) ); 12746372Sbostic if ( ndx != 1 ) newoff = bp[ndx-1]; 12846372Sbostic else newoff = hashp->BSIZE; 12946372Sbostic pairlen = newoff - bp[ndx+1]; 13046372Sbostic 13146372Sbostic if ( ndx != (n-1) ) { 13246372Sbostic /* Hard Case -- need to shuffle keys */ 13346372Sbostic register int i; 13446372Sbostic register char *src = bufp->page + (int)OFFSET(bp); 13546372Sbostic register char *dst = src + (int)pairlen; 13646372Sbostic bcopy ( src, dst, bp[ndx+1] - OFFSET(bp) ); 13746372Sbostic 13846372Sbostic /* Now adjust the pointers */ 13946372Sbostic for ( i = ndx+2; i <= n; i += 2 ) { 14046372Sbostic if ( bp[i+1] == OVFLPAGE ) { 14146372Sbostic bp[i-2] = bp[i]; 14246372Sbostic bp[i-1] = bp[i+1]; 14346372Sbostic } else { 14446372Sbostic bp[i-2] = bp[i] + pairlen; 14546372Sbostic bp[i-1] = bp[i+1] + pairlen; 14646372Sbostic } 14746372Sbostic } 14846372Sbostic } 14946372Sbostic 15046372Sbostic /* Finally adjust the page data */ 15146372Sbostic bp[n] = OFFSET(bp) + pairlen; 15246372Sbostic bp[n-1] = bp[n+1] + pairlen + 2 * sizeof(u_short); 15346372Sbostic bp[0] = n-2; 15446372Sbostic hashp->NKEYS--; 15546372Sbostic 15646372Sbostic bufp->flags |= BUF_MOD; 15746372Sbostic return (0); 15846372Sbostic } 15946372Sbostic /* 16046372Sbostic -1 ==> Error 16146372Sbostic 0 ==> OK 16246372Sbostic */ 16346372Sbostic extern int 16446372Sbostic __split_page(obucket, nbucket) 16546372Sbostic int obucket; 16646372Sbostic int nbucket; 16746372Sbostic { 16846372Sbostic DBT key; 16946372Sbostic DBT val; 17046372Sbostic 17146372Sbostic register BUFHEAD *new_bufp; 17246372Sbostic register BUFHEAD *old_bufp; 17346372Sbostic register u_short *ino; 17446372Sbostic register char *np; 17546460Sbostic int n; 17646460Sbostic int ndx; 17746460Sbostic int retval; 17846372Sbostic char *op; 17946372Sbostic 18046372Sbostic u_short copyto = (u_short)hashp->BSIZE; 18146460Sbostic u_short diff; 18246372Sbostic u_short off = (u_short)hashp->BSIZE; 18346372Sbostic u_short moved; 18446372Sbostic 18546372Sbostic old_bufp = __get_buf ( obucket, NULL, 0 ); 18646372Sbostic new_bufp = __get_buf ( nbucket, NULL, 0 ); 18746372Sbostic 18846460Sbostic old_bufp->flags |= (BUF_MOD|BUF_PIN); 18946460Sbostic new_bufp->flags |= (BUF_MOD|BUF_PIN); 19046372Sbostic 19146372Sbostic ino = (u_short *)(op = old_bufp->page); 19246372Sbostic np = new_bufp->page; 19346372Sbostic 19446372Sbostic moved = 0; 19546372Sbostic 19646372Sbostic for (n = 1, ndx = 1; n < ino[0]; n+=2) { 19746372Sbostic if ( ino[n+1] < REAL_KEY ) { 19846460Sbostic retval = ugly_split( obucket, old_bufp, new_bufp, 19946460Sbostic copyto, moved ); 20046460Sbostic old_bufp->flags &= ~BUF_PIN; 20146460Sbostic new_bufp->flags &= ~BUF_PIN; 20246460Sbostic return(retval); 20346460Sbostic 20446372Sbostic } 20546372Sbostic key.data = op + ino[n]; 20646372Sbostic key.size = off - ino[n]; 20746372Sbostic 20846372Sbostic if ( __call_hash ( key.data, key.size ) == obucket ) { 20946372Sbostic /* Don't switch page */ 21046372Sbostic diff = copyto - off; 21146372Sbostic if ( diff ) { 21246372Sbostic copyto = ino[n+1] + diff; 21346372Sbostic bcopy ( op + ino[n+1], op + copyto, off-ino[n+1]); 21446372Sbostic ino[ndx] = copyto + ino[n] - ino[n+1]; 21546372Sbostic ino[ndx+1] = copyto; 21646372Sbostic } else copyto = ino[n+1]; 21746372Sbostic ndx += 2; 21846372Sbostic } else { 21946372Sbostic /* Switch page */ 22046372Sbostic val.data = op + ino[n+1]; 22146372Sbostic val.size = ino[n] - ino[n+1]; 22246372Sbostic putpair( np, &key, &val); 22346372Sbostic moved +=2; 22446372Sbostic } 22546372Sbostic 22646372Sbostic off = ino[n+1]; 22746372Sbostic } 22846372Sbostic 22946372Sbostic /* Now clean up the page */ 23046372Sbostic ino[0] -= moved; 23146372Sbostic FREESPACE(ino) = copyto - sizeof(u_short) * (ino[0]+3); 23246372Sbostic OFFSET(ino) = copyto; 23346372Sbostic 23446372Sbostic #ifdef DEBUG3 23546372Sbostic fprintf(stderr, "split %d/%d\n", 23646372Sbostic ((u_short *) np)[0] / 2, 23746372Sbostic ((u_short *) op)[0] / 2); 23846372Sbostic #endif 23946460Sbostic /* unpin both pages */ 24046460Sbostic old_bufp->flags &= ~BUF_PIN; 24146460Sbostic new_bufp->flags &= ~BUF_PIN; 24246372Sbostic return(0); 24346372Sbostic } 24446372Sbostic /* 24546372Sbostic 0 ==> success 24646372Sbostic -1 ==> failure 24746372Sbostic 24846460Sbostic Called when we encounter an overflow or big key/data page during 24946460Sbostic split handling. 25046460Sbostic This is special cased since we have to begin checking whether 25146372Sbostic the key/data pairs fit on their respective pages and because 25246372Sbostic we may need overflow pages for both the old and new pages 25346460Sbostic 25446460Sbostic The first page might be a page with regular key/data pairs 25546460Sbostic in which case we have a regular overflow condition and just 25646460Sbostic need to go on to the next page or it might be a big key/data 25746460Sbostic pair in which case we need to fix the big key/data pair. 25846372Sbostic */ 25946372Sbostic static int 26046372Sbostic ugly_split( obucket, old_bufp, new_bufp, copyto, moved ) 26146372Sbostic int obucket; /* Same as __split_page */ 26246372Sbostic BUFHEAD *old_bufp; 26346372Sbostic BUFHEAD *new_bufp; 26446372Sbostic u_short copyto; /* First byte on page which contains key/data values */ 26546372Sbostic int moved; /* number of pairs moved to new page */ 26646372Sbostic { 26746372Sbostic register BUFHEAD *bufp = old_bufp; /* Buffer header for ino */ 26846372Sbostic register u_short *ino = (u_short *)old_bufp->page; 26946372Sbostic /* Page keys come off of */ 27046372Sbostic register u_short *np = (u_short *)new_bufp->page; /* New page */ 27146372Sbostic register u_short *op = (u_short *)old_bufp->page; 27246372Sbostic /* Page keys go on to if they 27346372Sbostic aren't moving */ 27446372Sbostic 27546372Sbostic char *cino; /* Character value of ino */ 27646372Sbostic BUFHEAD *last_bfp = NULL; /* Last buffer header OVFL which 27746372Sbostic needs to be freed */ 27846372Sbostic u_short ov_addr, last_addr = 0; 27946372Sbostic u_short n; 28046372Sbostic u_short off; 28146372Sbostic 28246372Sbostic DBT key, val; 28346372Sbostic SPLIT_RETURN ret; 28446372Sbostic 28546372Sbostic n = ino[0]-1; 28646372Sbostic while ( n < ino[0] ) { 28746502Sbostic if ( ino[2] < REAL_KEY && ino[2] != OVFLPAGE ) { 28846502Sbostic if (__big_split (old_bufp, new_bufp, bufp, ov_addr, obucket, &ret)) { 28946502Sbostic return(-1); 29046502Sbostic } 29146502Sbostic old_bufp = ret.oldp; 29246502Sbostic if ( !old_bufp ) return(-1); 29346502Sbostic op = (u_short *)old_bufp->page; 29446502Sbostic new_bufp = ret.newp; 29546502Sbostic if ( !new_bufp ) return(-1); 29646502Sbostic np = (u_short *)new_bufp->page; 29746502Sbostic bufp = ret.nextp; 29846502Sbostic if ( !bufp ) return(0); 29946502Sbostic cino = (char *)bufp->page; 30046502Sbostic ino = (u_short *)cino; 30146502Sbostic last_bfp = ret.nextp; 30246502Sbostic } else if ( ino[n+1] == OVFLPAGE ) { 30346372Sbostic ov_addr = ino[n]; 30446372Sbostic /* 30546372Sbostic Fix up the old page -- the extra 2 are the fields which 30646372Sbostic contained the overflow information 30746372Sbostic */ 30846372Sbostic ino[0] -= (moved + 2); 30946372Sbostic FREESPACE(ino) = copyto - sizeof(u_short) * (ino[0]+3); 31046372Sbostic OFFSET(ino) = copyto; 31146372Sbostic 31246372Sbostic bufp = __get_buf ( ov_addr, bufp, 0 ); 31346372Sbostic if ( !bufp ) return(-1); 31446372Sbostic 31546372Sbostic ino = (u_short *)bufp->page; 31646372Sbostic n = 1; 31746372Sbostic copyto = hashp->BSIZE; 31846372Sbostic moved = 0; 31946372Sbostic 32046372Sbostic if ( last_bfp ) { 32146372Sbostic __free_ovflpage( last_bfp); 32246372Sbostic } 32346372Sbostic last_bfp = bufp; 32446372Sbostic } 32546372Sbostic 32646372Sbostic 32746460Sbostic /* Move regular sized pairs of there are any */ 32846372Sbostic off = hashp->BSIZE; 32946372Sbostic for ( n = 1; (n < ino[0]) && (ino[n+1] >= REAL_KEY); n += 2 ) { 33046372Sbostic cino = (char *)ino; 33146372Sbostic key.data = cino + ino[n]; 33246372Sbostic key.size = off - ino[n]; 33346372Sbostic val.data = cino + ino[n+1]; 33446372Sbostic val.size = ino[n] - ino[n+1]; 33546372Sbostic off = ino[n+1]; 33646372Sbostic 33746372Sbostic if ( __call_hash ( key.data, key.size ) == obucket ) { 33846372Sbostic /* Keep on old page */ 33946372Sbostic if (PAIRFITS(op,(&key),(&val))) putpair((char *)op, &key, &val); 34046372Sbostic else { 34146372Sbostic old_bufp = __add_ovflpage ( old_bufp ); 34246372Sbostic if ( !old_bufp ) return(-1); 34346372Sbostic op = (u_short *)old_bufp->page; 34446372Sbostic putpair ((char *)op, &key, &val); 34546372Sbostic } 34646372Sbostic old_bufp->flags |= BUF_MOD; 34746372Sbostic } else { 34846372Sbostic /* Move to new page */ 34946372Sbostic if (PAIRFITS(np,(&key),(&val))) putpair((char *)np, &key, &val); 35046372Sbostic else { 35146372Sbostic new_bufp = __add_ovflpage ( new_bufp ); 35246372Sbostic if ( !new_bufp )return(-1); 35346372Sbostic np = (u_short *)new_bufp->page; 35446372Sbostic putpair ((char *)np, &key, &val); 35546372Sbostic } 35646372Sbostic new_bufp->flags |= BUF_MOD; 35746372Sbostic } 35846372Sbostic } 35946372Sbostic } 36046372Sbostic if ( last_bfp ) { 36146372Sbostic __free_ovflpage(last_bfp); 36246372Sbostic } 36346372Sbostic 36446372Sbostic return (0); 36546372Sbostic } 36646372Sbostic /* 36746372Sbostic Add the given pair to the page 36846372Sbostic 1 ==> failure 36946372Sbostic 0 ==> OK 37046372Sbostic */ 37146372Sbostic extern int 37246372Sbostic __addel(bufp, key, val) 37346372Sbostic BUFHEAD *bufp; 37446372Sbostic DBT *key; 37546372Sbostic DBT *val; 37646372Sbostic { 37746372Sbostic register u_short *bp = (u_short *)bufp->page; 37846372Sbostic register u_short *sop; 37946372Sbostic int do_expand; 38046372Sbostic 38146372Sbostic do_expand = 0; 38246372Sbostic while ( bp[0] && (bp[bp[0]] < REAL_KEY) ) { 38346372Sbostic /* Exception case */ 38446372Sbostic if ( bp[2] < REAL_KEY ) { 38546372Sbostic /* This is a big-keydata pair */ 38646372Sbostic bufp = __add_ovflpage(bufp); 38746372Sbostic if ( !bufp ) { 38846372Sbostic return(-1); 38946372Sbostic } 39046372Sbostic bp = (u_short *)bufp->page; 39146372Sbostic } else { 39246372Sbostic /* Try to squeeze key on this page */ 39346372Sbostic if ( FREESPACE(bp) > PAIRSIZE(key,val) ) { 39446372Sbostic squeeze_key ( bp, key, val ); 39546372Sbostic return(0); 39646372Sbostic } else { 39746372Sbostic bufp = __get_buf ( bp[bp[0]-1], bufp, 0 ); 39846372Sbostic if (!bufp) { 39946372Sbostic return(-1); 40046372Sbostic } 40146372Sbostic bp = (u_short *)bufp->page; 40246372Sbostic } 40346372Sbostic } 40446372Sbostic } 40546372Sbostic 40646372Sbostic if ( PAIRFITS(bp,key,val) ) putpair (bufp->page, key, val); 40746372Sbostic else { 40846372Sbostic do_expand = 1; 40946372Sbostic bufp = __add_ovflpage ( bufp ); 41046372Sbostic if (!bufp)return(-1); 41146372Sbostic sop = (u_short *) bufp->page; 41246372Sbostic 41346372Sbostic if ( PAIRFITS(sop, key, val) ) putpair ( (char *)sop, key, val ); 41446372Sbostic else if ( __big_insert ( bufp, key, val ) ) { 41546372Sbostic return(-1); 41646372Sbostic } 41746372Sbostic } 41846372Sbostic bufp->flags |= BUF_MOD; 41946372Sbostic /* 42046372Sbostic If the average number of keys per bucket exceeds the fill factor, 42146372Sbostic expand the table 42246372Sbostic */ 42346372Sbostic hashp->NKEYS++; 42446372Sbostic if (do_expand || 42546372Sbostic (hashp->NKEYS / (hashp->MAX_BUCKET+1) > hashp->FFACTOR) ) { 42646372Sbostic return(__expand_table()); 42746372Sbostic } 42846372Sbostic return(0); 42946372Sbostic } 43046372Sbostic 43146372Sbostic /* 43246372Sbostic returns a pointer, NULL on error 43346372Sbostic */ 43446372Sbostic extern BUFHEAD * 43546372Sbostic __add_ovflpage ( bufp ) 43646372Sbostic BUFHEAD *bufp; 43746372Sbostic { 43846372Sbostic register u_short *sp = (u_short *)bufp->page; 43946372Sbostic 44046372Sbostic u_short ovfl_num; 44146372Sbostic u_short ndx, newoff; 44246372Sbostic char *op; 44346372Sbostic DBT okey, oval; 44446372Sbostic #ifdef DEBUG1 44546372Sbostic int tmp1, tmp2; 44646372Sbostic #endif 44746372Sbostic 44846372Sbostic bufp->flags |= BUF_MOD; 44946372Sbostic ovfl_num = overflow_page (); 45046372Sbostic #ifdef DEBUG1 45146372Sbostic tmp1 = bufp->addr; 45246372Sbostic tmp2 = bufp->ovfl?bufp->ovfl->addr:0; 45346372Sbostic #endif 45446372Sbostic if (!ovfl_num || !(bufp->ovfl = __get_buf ( ovfl_num, bufp, 1 ))) { 45546372Sbostic return(NULL); 45646372Sbostic } 45746372Sbostic bufp->ovfl->flags |= BUF_MOD; 45846372Sbostic #ifdef DEBUG1 45946372Sbostic fprintf ( stderr, "ADDOVFLPAGE: %d->ovfl was %d is now %d\n", tmp1, tmp2, 46046372Sbostic bufp->ovfl->addr ); 46146372Sbostic #endif 46246372Sbostic ndx = sp[0]; 46346372Sbostic /* 46446372Sbostic Since a pair is allocated on a page only if there's room 46546372Sbostic to add an overflow page, we know that the OVFL information 46646372Sbostic will fit on the page 46746372Sbostic */ 46846372Sbostic sp[ndx+4] = OFFSET(sp); 46946372Sbostic sp[ndx+3] = FREESPACE(sp) - OVFLSIZE; 47046372Sbostic sp[ndx+1] = ovfl_num; 47146372Sbostic sp[ndx+2] = OVFLPAGE; 47246372Sbostic sp[0] = ndx+2; 47346372Sbostic #ifdef HASH_STATISTICS 47446372Sbostic hash_overflows++; 47546372Sbostic #endif 47646372Sbostic return(bufp->ovfl); 47746372Sbostic } 47846372Sbostic 47946372Sbostic /* 48046372Sbostic 0 indicates SUCCESS 48146372Sbostic -1 indicates FAILURE 48246372Sbostic */ 48346372Sbostic extern int 48446372Sbostic __get_page ( p, bucket, is_bucket, is_disk, is_bitmap ) 48546372Sbostic char *p; 48646372Sbostic int bucket; 48746372Sbostic int is_bucket; 48846372Sbostic int is_disk; 48946372Sbostic int is_bitmap; 49046372Sbostic { 49146372Sbostic register int size; 49246372Sbostic register int fd; 49346372Sbostic register int page; 49446372Sbostic u_short *bp; 49546372Sbostic int rsize; 49646372Sbostic 49746372Sbostic fd = hashp->fp; 49846372Sbostic size = hashp->BSIZE; 49946372Sbostic 50046502Sbostic if ( (fd == -1) || !is_disk ) { 50146372Sbostic PAGE_INIT(p); 50246372Sbostic return(0); 50346372Sbostic } 50446372Sbostic 50546372Sbostic if ( is_bucket) page = BUCKET_TO_PAGE (bucket); 50646372Sbostic else page = OADDR_TO_PAGE (bucket); 50746500Sbostic if ((lseek ( fd, page << hashp->BSHIFT, SEEK_SET ) == -1) || 50846372Sbostic ((rsize = read ( fd, p, size )) == -1 )) { 50946372Sbostic return(-1); 51046372Sbostic } 51146372Sbostic bp = (u_short *)p; 51246372Sbostic if ( !rsize ) { 51346372Sbostic bp[0] = 0; /* We hit the EOF, so initialize a new page */ 51446372Sbostic } else if ( rsize != size ) { 51546372Sbostic errno = EFTYPE; 51646372Sbostic return(-1); 51746372Sbostic } 51846372Sbostic if (!bp[0]) { 51946372Sbostic PAGE_INIT(p); 52046372Sbostic } else if ( hashp->LORDER != BYTE_ORDER ) { 52146372Sbostic register int i; 52246372Sbostic register int max; 52346372Sbostic 52446372Sbostic if ( is_bitmap ) { 52546372Sbostic max = hashp->BSIZE >> 2; /* divide by 4 */ 52646372Sbostic for ( i=0; i < max; i++ ) { 52746372Sbostic BLSWAP(((long *)p)[i]); 52846372Sbostic } 52946372Sbostic } else { 53046372Sbostic BSSWAP(bp[0]); 53146372Sbostic max = bp[0] + 2; 53246372Sbostic for ( i=1; i <= max; i++ ) { 53346372Sbostic BSSWAP(bp[i]); 53446372Sbostic } 53546372Sbostic } 53646372Sbostic } 53746372Sbostic return (0); 53846372Sbostic } 53946372Sbostic 54046372Sbostic /* 54146372Sbostic Write page p to disk 54246372Sbostic -1==>failure 54346372Sbostic 0==> OK 54446372Sbostic */ 54546372Sbostic extern int 54646372Sbostic __put_page ( p, bucket, is_bucket, is_bitmap ) 54746372Sbostic char *p; 54846372Sbostic int bucket; 54946372Sbostic int is_bucket; 55046372Sbostic int is_bitmap; 55146372Sbostic { 55246372Sbostic register int size; 55346372Sbostic register int fd; 55446372Sbostic register int page; 55546372Sbostic int wsize; 55646372Sbostic 55746372Sbostic size = hashp->BSIZE; 55846460Sbostic if ( (hashp->fp == -1) && open_temp() ) return (1); 55946372Sbostic fd = hashp->fp; 56046372Sbostic 56146372Sbostic if ( hashp->LORDER != BYTE_ORDER ) { 56246372Sbostic register int i; 56346372Sbostic register int max; 56446372Sbostic 56546372Sbostic if ( is_bitmap ) { 56646372Sbostic max = hashp->BSIZE >> 2; /* divide by 4 */ 56746372Sbostic for ( i=0; i < max; i++ ) { 56846372Sbostic BLSWAP(((long *)p)[i]); 56946372Sbostic } 57046372Sbostic } else { 57146372Sbostic max = ((u_short *)p)[0] + 2; 57246372Sbostic for ( i=0; i <= max; i++ ) { 57346372Sbostic BSSWAP(((u_short *)p)[i]); 57446372Sbostic } 57546372Sbostic } 57646372Sbostic } 57746372Sbostic if (is_bucket ) page = BUCKET_TO_PAGE (bucket); 57846372Sbostic else page = OADDR_TO_PAGE ( bucket ); 57946500Sbostic if ((lseek ( fd, page << hashp->BSHIFT, SEEK_SET ) == -1) || 58046372Sbostic ((wsize = write ( fd, p, size )) == -1 )) { 58146372Sbostic /* Errno is set */ 58246372Sbostic return(-1); 58346372Sbostic } 58446372Sbostic if ( wsize != size ) { 58546372Sbostic errno = EFTYPE; 58646372Sbostic return(-1); 58746372Sbostic } 58846372Sbostic return(0); 58946372Sbostic } 59046372Sbostic #define BYTE_MASK ((1 << INT_BYTE_SHIFT) -1) 59146372Sbostic /* 59246372Sbostic Initialize a new bitmap page. Bitmap pages are left in memory 59346372Sbostic once they are read in. 59446372Sbostic */ 59546372Sbostic extern u_long * 59646372Sbostic __init_bitmap(pnum, nbits, ndx) 59746372Sbostic u_short pnum; 59846372Sbostic int nbits; 59946372Sbostic int ndx; 60046372Sbostic { 60146372Sbostic u_long *ip; 60246372Sbostic int clearints; 60346372Sbostic int clearbytes; 60446372Sbostic 60546372Sbostic if ( !(ip = (u_long *)malloc (hashp->BSIZE)) ) return (NULL); 60646505Sbostic hashp->exmaps++; 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; 78446499Sbostic 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