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