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