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