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*55315Sbostic static char sccsid[] = "@(#)hash_page.c 5.22 (Berkeley) 07/17/92"; 1346372Sbostic #endif /* LIBC_SCCS and not lint */ 1446372Sbostic 1550997Sbostic /* 1650997Sbostic * PACKAGE: hashing 1750997Sbostic * 1850997Sbostic * DESCRIPTION: 1950997Sbostic * Page manipulation for hashing package. 2050997Sbostic * 2150997Sbostic * ROUTINES: 2250997Sbostic * 2350997Sbostic * External 2450997Sbostic * __get_page 2550997Sbostic * __add_ovflpage 2650997Sbostic * Internal 2750997Sbostic * overflow_page 2850997Sbostic * open_temp 2950997Sbostic */ 3046372Sbostic 3146372Sbostic #include <sys/param.h> 3246562Sbostic #include <fcntl.h> 3346416Sbostic #include <signal.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> 4050997Sbostic #ifdef DEBUG 4150997Sbostic #include <assert.h> 4250997Sbostic #endif 4346372Sbostic #include "hash.h" 4446372Sbostic #include "page.h" 4550997Sbostic #include "extern.h" 4646372Sbostic 4751055Sbostic static u_long *fetch_bitmap __P((int)); 4851055Sbostic static u_long first_free __P((u_long)); 4951055Sbostic static int open_temp __P((void)); 5051055Sbostic static u_short overflow_page __P((void)); 5151055Sbostic static void putpair __P((char *, const DBT *, const DBT *)); 5251055Sbostic static void squeeze_key __P((u_short *, const DBT *, const DBT *)); 5351055Sbostic static int ugly_split __P((u_int, BUFHEAD *, BUFHEAD *, int, int)); 5446372Sbostic 5550997Sbostic #define PAGE_INIT(P) { \ 5650997Sbostic ((u_short *)(P))[0] = 0; \ 5750997Sbostic ((u_short *)(P))[1] = hashp->BSIZE - 3 * sizeof(u_short); \ 5850997Sbostic ((u_short *)(P))[2] = hashp->BSIZE; \ 5946372Sbostic } 6046372Sbostic 6146372Sbostic /* 6250997Sbostic * This is called AFTER we have verified that there is room on the page for 6350997Sbostic * the pair (PAIRFITS has returned true) so we go right ahead and start moving 6450997Sbostic * stuff on. 6550997Sbostic */ 6646372Sbostic static void 6746372Sbostic putpair(p, key, val) 6850997Sbostic char *p; 6950997Sbostic const DBT *key, *val; 7046372Sbostic { 7150997Sbostic register u_short *bp, n, off; 7246372Sbostic 7350997Sbostic bp = (u_short *)p; 7450997Sbostic 7550997Sbostic /* Enter the key first. */ 7646372Sbostic n = bp[0]; 7746372Sbostic 7846372Sbostic off = OFFSET(bp) - key->size; 7950997Sbostic bcopy(key->data, p + off, key->size); 8046372Sbostic bp[++n] = off; 8146372Sbostic 8250997Sbostic /* Now the data. */ 8346372Sbostic off -= val->size; 8450997Sbostic bcopy(val->data, p + off, val->size); 8546372Sbostic bp[++n] = off; 8646372Sbostic 8750997Sbostic /* Adjust page info. */ 8846372Sbostic bp[0] = n; 8950997Sbostic bp[n + 1] = off - ((n + 3) * sizeof(u_short)); 9050997Sbostic bp[n + 2] = off; 9146372Sbostic } 9250997Sbostic 9346372Sbostic /* 9450997Sbostic * Returns: 9550997Sbostic * 0 OK 9650997Sbostic * -1 error 9750997Sbostic */ 9846372Sbostic extern int 9946372Sbostic __delpair(bufp, ndx) 10050997Sbostic BUFHEAD *bufp; 10150997Sbostic register int ndx; 10246372Sbostic { 10350997Sbostic register u_short *bp, newoff; 10450997Sbostic register int n; 10546372Sbostic u_short pairlen; 10646372Sbostic 10750997Sbostic bp = (u_short *)bufp->page; 10850997Sbostic n = bp[0]; 10946372Sbostic 11050997Sbostic if (bp[ndx + 1] < REAL_KEY) 11151055Sbostic return (__big_delete(bufp)); 11250997Sbostic if (ndx != 1) 11350997Sbostic newoff = bp[ndx - 1]; 11450997Sbostic else 11550997Sbostic newoff = hashp->BSIZE; 11650997Sbostic pairlen = newoff - bp[ndx + 1]; 11750997Sbostic 11850997Sbostic if (ndx != (n - 1)) { 11946372Sbostic /* Hard Case -- need to shuffle keys */ 12046372Sbostic register int i; 12146372Sbostic register char *src = bufp->page + (int)OFFSET(bp); 12246372Sbostic register char *dst = src + (int)pairlen; 12350997Sbostic bcopy(src, dst, bp[ndx + 1] - OFFSET(bp)); 12446372Sbostic 12546372Sbostic /* Now adjust the pointers */ 12650997Sbostic for (i = ndx + 2; i <= n; i += 2) { 12750997Sbostic if (bp[i + 1] == OVFLPAGE) { 12850997Sbostic bp[i - 2] = bp[i]; 12950997Sbostic bp[i - 1] = bp[i + 1]; 13050997Sbostic } else { 13150997Sbostic bp[i - 2] = bp[i] + pairlen; 13250997Sbostic bp[i - 1] = bp[i + 1] + pairlen; 13350997Sbostic } 13446372Sbostic } 13546372Sbostic } 13646372Sbostic /* Finally adjust the page data */ 13746372Sbostic bp[n] = OFFSET(bp) + pairlen; 13850997Sbostic bp[n - 1] = bp[n + 1] + pairlen + 2 * sizeof(u_short); 13950997Sbostic bp[0] = n - 2; 14046372Sbostic hashp->NKEYS--; 14146372Sbostic 14246372Sbostic bufp->flags |= BUF_MOD; 14346372Sbostic return (0); 14446372Sbostic } 14546372Sbostic /* 14650997Sbostic * Returns: 14750997Sbostic * 0 ==> OK 14850997Sbostic * -1 ==> Error 14950997Sbostic */ 15046372Sbostic extern int 15146372Sbostic __split_page(obucket, nbucket) 15250997Sbostic u_int obucket, nbucket; 15346372Sbostic { 15450997Sbostic register BUFHEAD *new_bufp, *old_bufp; 15546372Sbostic register u_short *ino; 15650997Sbostic register char *np; 15750997Sbostic DBT key, val; 15850997Sbostic int n, ndx, retval; 15950997Sbostic u_short copyto, diff, off, moved; 16050997Sbostic char *op; 16146372Sbostic 16250997Sbostic copyto = (u_short)hashp->BSIZE; 16350997Sbostic off = (u_short)hashp->BSIZE; 16450997Sbostic old_bufp = __get_buf(obucket, NULL, 0); 16553870Sbostic if (old_bufp == NULL) 16653870Sbostic return (-1); 16750997Sbostic new_bufp = __get_buf(nbucket, NULL, 0); 16853870Sbostic if (new_bufp == NULL) 16953870Sbostic return (-1); 17046372Sbostic 17150997Sbostic old_bufp->flags |= (BUF_MOD | BUF_PIN); 17250997Sbostic new_bufp->flags |= (BUF_MOD | BUF_PIN); 17346372Sbostic 17446372Sbostic ino = (u_short *)(op = old_bufp->page); 17546372Sbostic np = new_bufp->page; 17646372Sbostic 17746372Sbostic moved = 0; 17846372Sbostic 17950997Sbostic for (n = 1, ndx = 1; n < ino[0]; n += 2) { 18050997Sbostic if (ino[n + 1] < REAL_KEY) { 18150997Sbostic retval = ugly_split(obucket, old_bufp, new_bufp, 18251055Sbostic (int)copyto, (int)moved); 18350997Sbostic old_bufp->flags &= ~BUF_PIN; 18450997Sbostic new_bufp->flags &= ~BUF_PIN; 18550997Sbostic return (retval); 18650997Sbostic 18746372Sbostic } 18850997Sbostic key.data = (u_char *)op + ino[n]; 18946372Sbostic key.size = off - ino[n]; 19046372Sbostic 19150997Sbostic if (__call_hash(key.data, key.size) == obucket) { 19250997Sbostic /* Don't switch page */ 19350997Sbostic diff = copyto - off; 19450997Sbostic if (diff) { 19550997Sbostic copyto = ino[n + 1] + diff; 19650997Sbostic bcopy(op + ino[n + 1], op + copyto, 19750997Sbostic off - ino[n + 1]); 19850997Sbostic ino[ndx] = copyto + ino[n] - ino[n + 1]; 19950997Sbostic ino[ndx + 1] = copyto; 20050997Sbostic } else 20150997Sbostic copyto = ino[n + 1]; 20250997Sbostic ndx += 2; 20346372Sbostic } else { 20450997Sbostic /* Switch page */ 20550997Sbostic val.data = (u_char *)op + ino[n + 1]; 20650997Sbostic val.size = ino[n] - ino[n + 1]; 20750997Sbostic putpair(np, &key, &val); 20850997Sbostic moved += 2; 20946372Sbostic } 21046372Sbostic 21150997Sbostic off = ino[n + 1]; 21246372Sbostic } 21346372Sbostic 21446372Sbostic /* Now clean up the page */ 21546372Sbostic ino[0] -= moved; 21650997Sbostic FREESPACE(ino) = copyto - sizeof(u_short) * (ino[0] + 3); 21746372Sbostic OFFSET(ino) = copyto; 21846372Sbostic 21946372Sbostic #ifdef DEBUG3 22050997Sbostic (void)fprintf(stderr, "split %d/%d\n", 22150997Sbostic ((u_short *)np)[0] / 2, 22250997Sbostic ((u_short *)op)[0] / 2); 22346372Sbostic #endif 22446460Sbostic /* unpin both pages */ 22546460Sbostic old_bufp->flags &= ~BUF_PIN; 22646460Sbostic new_bufp->flags &= ~BUF_PIN; 22750997Sbostic return (0); 22846372Sbostic } 22950997Sbostic 23046372Sbostic /* 23150997Sbostic * Called when we encounter an overflow or big key/data page during split 23250997Sbostic * handling. This is special cased since we have to begin checking whether 23350997Sbostic * the key/data pairs fit on their respective pages and because we may need 23450997Sbostic * overflow pages for both the old and new pages. 23550997Sbostic * 23650997Sbostic * The first page might be a page with regular key/data pairs in which case 23750997Sbostic * we have a regular overflow condition and just need to go on to the next 23850997Sbostic * page or it might be a big key/data pair in which case we need to fix the 23950997Sbostic * big key/data pair. 24050997Sbostic * 24150997Sbostic * Returns: 24250997Sbostic * 0 ==> success 24350997Sbostic * -1 ==> failure 24450997Sbostic */ 24546372Sbostic static int 24650997Sbostic ugly_split(obucket, old_bufp, new_bufp, copyto, moved) 24750997Sbostic u_int obucket; /* Same as __split_page. */ 24850997Sbostic BUFHEAD *old_bufp, *new_bufp; 24950997Sbostic int copyto; /* First byte on page which contains key/data values. */ 25050997Sbostic int moved; /* Number of pairs moved to new page. */ 25146372Sbostic { 25250997Sbostic register BUFHEAD *bufp; /* Buffer header for ino */ 25350997Sbostic register u_short *ino; /* Page keys come off of */ 25450997Sbostic register u_short *np; /* New page */ 25550997Sbostic register u_short *op; /* Page keys go on to if they aren't moving */ 25646372Sbostic 25750997Sbostic BUFHEAD *last_bfp; /* Last buf header OVFL needing to be freed */ 25850997Sbostic DBT key, val; 25950997Sbostic SPLIT_RETURN ret; 26051055Sbostic u_short n, off, ov_addr, scopyto; 26150997Sbostic char *cino; /* Character value of ino */ 26246372Sbostic 26350997Sbostic bufp = old_bufp; 26450997Sbostic ino = (u_short *)old_bufp->page; 26550997Sbostic np = (u_short *)new_bufp->page; 26650997Sbostic op = (u_short *)old_bufp->page; 26750997Sbostic last_bfp = NULL; 26850997Sbostic scopyto = (u_short)copyto; /* ANSI */ 26946372Sbostic 27050997Sbostic n = ino[0] - 1; 27150997Sbostic while (n < ino[0]) { 27250997Sbostic if (ino[2] < REAL_KEY && ino[2] != OVFLPAGE) { 27351055Sbostic /* 27451055Sbostic * Ov_addr gets set before reaching this point; there's 27551055Sbostic * always an overflow page before a big key/data page. 27651055Sbostic */ 27750997Sbostic if (__big_split(old_bufp, 27850997Sbostic new_bufp, bufp, ov_addr, obucket, &ret)) 27950997Sbostic return (-1); 28050997Sbostic old_bufp = ret.oldp; 28150997Sbostic if (!old_bufp) 28250997Sbostic return (-1); 28350997Sbostic op = (u_short *)old_bufp->page; 28450997Sbostic new_bufp = ret.newp; 28550997Sbostic if (!new_bufp) 28650997Sbostic return (-1); 28750997Sbostic np = (u_short *)new_bufp->page; 28850997Sbostic bufp = ret.nextp; 28950997Sbostic if (!bufp) 29050997Sbostic return (0); 29150997Sbostic cino = (char *)bufp->page; 29250997Sbostic ino = (u_short *)cino; 29350997Sbostic last_bfp = ret.nextp; 29450997Sbostic } else if (ino[n + 1] == OVFLPAGE) { 29550997Sbostic ov_addr = ino[n]; 29650997Sbostic /* 29750997Sbostic * Fix up the old page -- the extra 2 are the fields 29850997Sbostic * which contained the overflow information. 29950997Sbostic */ 30050997Sbostic ino[0] -= (moved + 2); 30150997Sbostic FREESPACE(ino) = 30250997Sbostic scopyto - sizeof(u_short) * (ino[0] + 3); 30350997Sbostic OFFSET(ino) = scopyto; 30446372Sbostic 30550997Sbostic bufp = __get_buf(ov_addr, bufp, 0); 30650997Sbostic if (!bufp) 30750997Sbostic return (-1); 30846372Sbostic 30950997Sbostic ino = (u_short *)bufp->page; 31050997Sbostic n = 1; 31150997Sbostic scopyto = hashp->BSIZE; 31250997Sbostic moved = 0; 31346372Sbostic 31450997Sbostic if (last_bfp) 31550997Sbostic __free_ovflpage(last_bfp); 31650997Sbostic last_bfp = bufp; 31750997Sbostic } 31850997Sbostic /* Move regular sized pairs of there are any */ 31950997Sbostic off = hashp->BSIZE; 32050997Sbostic for (n = 1; (n < ino[0]) && (ino[n + 1] >= REAL_KEY); n += 2) { 32150997Sbostic cino = (char *)ino; 32250997Sbostic key.data = (u_char *)cino + ino[n]; 32350997Sbostic key.size = off - ino[n]; 32450997Sbostic val.data = (u_char *)cino + ino[n + 1]; 32550997Sbostic val.size = ino[n] - ino[n + 1]; 32650997Sbostic off = ino[n + 1]; 32746372Sbostic 32850997Sbostic if (__call_hash(key.data, key.size) == obucket) { 32950997Sbostic /* Keep on old page */ 33050997Sbostic if (PAIRFITS(op, (&key), (&val))) 33150997Sbostic putpair((char *)op, &key, &val); 33250997Sbostic else { 33350997Sbostic old_bufp = __add_ovflpage(old_bufp); 33450997Sbostic if (!old_bufp) 33550997Sbostic return (-1); 33650997Sbostic op = (u_short *)old_bufp->page; 33750997Sbostic putpair((char *)op, &key, &val); 33850997Sbostic } 33950997Sbostic old_bufp->flags |= BUF_MOD; 34050997Sbostic } else { 34150997Sbostic /* Move to new page */ 34250997Sbostic if (PAIRFITS(np, (&key), (&val))) 34350997Sbostic putpair((char *)np, &key, &val); 34450997Sbostic else { 34550997Sbostic new_bufp = __add_ovflpage(new_bufp); 34650997Sbostic if (!new_bufp) 34750997Sbostic return (-1); 34850997Sbostic np = (u_short *)new_bufp->page; 34950997Sbostic putpair((char *)np, &key, &val); 35050997Sbostic } 35150997Sbostic new_bufp->flags |= BUF_MOD; 35250997Sbostic } 35346372Sbostic } 35446372Sbostic } 35550997Sbostic if (last_bfp) 35650997Sbostic __free_ovflpage(last_bfp); 35750997Sbostic return (0); 35850997Sbostic } 35946372Sbostic 36046372Sbostic /* 36150997Sbostic * Add the given pair to the page 36250997Sbostic * 36350997Sbostic * Returns: 36450997Sbostic * 0 ==> OK 36550997Sbostic * 1 ==> failure 36650997Sbostic */ 36746372Sbostic extern int 36846372Sbostic __addel(bufp, key, val) 36950997Sbostic BUFHEAD *bufp; 37050997Sbostic const DBT *key, *val; 37146372Sbostic { 37250997Sbostic register u_short *bp, *sop; 37350997Sbostic int do_expand; 37446372Sbostic 37550997Sbostic bp = (u_short *)bufp->page; 37650997Sbostic do_expand = 0; 37750997Sbostic while (bp[0] && (bp[bp[0]] < REAL_KEY)) 37850997Sbostic /* Exception case */ 37953500Sbostic if (bp[2] < REAL_KEY && bp[bp[0]] != OVFLPAGE) { 38050997Sbostic /* This is a big-keydata pair */ 38150997Sbostic bufp = __add_ovflpage(bufp); 38250997Sbostic if (!bufp) 38350997Sbostic return (-1); 38450997Sbostic bp = (u_short *)bufp->page; 38550997Sbostic } else 38650997Sbostic /* Try to squeeze key on this page */ 38750997Sbostic if (FREESPACE(bp) > PAIRSIZE(key, val)) { 38850997Sbostic squeeze_key(bp, key, val); 38950997Sbostic return (0); 39050997Sbostic } else { 39150997Sbostic bufp = __get_buf(bp[bp[0] - 1], bufp, 0); 39250997Sbostic if (!bufp) 39350997Sbostic return (-1); 39450997Sbostic bp = (u_short *)bufp->page; 39550997Sbostic } 39646372Sbostic 39750997Sbostic if (PAIRFITS(bp, key, val)) 39850997Sbostic putpair(bufp->page, key, val); 39950997Sbostic else { 40050997Sbostic do_expand = 1; 40150997Sbostic bufp = __add_ovflpage(bufp); 40250997Sbostic if (!bufp) 40350997Sbostic return (-1); 40450997Sbostic sop = (u_short *)bufp->page; 40546372Sbostic 40650997Sbostic if (PAIRFITS(sop, key, val)) 40750997Sbostic putpair((char *)sop, key, val); 40850997Sbostic else 40950997Sbostic if (__big_insert(bufp, key, val)) 41050997Sbostic return (-1); 41146372Sbostic } 41250997Sbostic bufp->flags |= BUF_MOD; 41350997Sbostic /* 41450997Sbostic * If the average number of keys per bucket exceeds the fill factor, 41550997Sbostic * expand the table. 41650997Sbostic */ 41750997Sbostic hashp->NKEYS++; 41850997Sbostic if (do_expand || 41950997Sbostic (hashp->NKEYS / (hashp->MAX_BUCKET + 1) > hashp->FFACTOR)) 42050997Sbostic return (__expand_table()); 42150997Sbostic return (0); 42246372Sbostic } 42346372Sbostic 42446372Sbostic /* 42550997Sbostic * 42650997Sbostic * Returns: 42750997Sbostic * pointer on success 42850997Sbostic * NULL on error 42950997Sbostic */ 43046372Sbostic extern BUFHEAD * 43150997Sbostic __add_ovflpage(bufp) 43250997Sbostic BUFHEAD *bufp; 43346372Sbostic { 43450997Sbostic register u_short *sp; 43550997Sbostic u_short ndx, ovfl_num; 43646372Sbostic #ifdef DEBUG1 43750997Sbostic int tmp1, tmp2; 43846372Sbostic #endif 43950997Sbostic sp = (u_short *)bufp->page; 44050997Sbostic bufp->flags |= BUF_MOD; 44150997Sbostic ovfl_num = overflow_page(); 44246372Sbostic #ifdef DEBUG1 44350997Sbostic tmp1 = bufp->addr; 44450997Sbostic tmp2 = bufp->ovfl ? bufp->ovfl->addr : 0; 44546372Sbostic #endif 44650997Sbostic if (!ovfl_num || !(bufp->ovfl = __get_buf(ovfl_num, bufp, 1))) 44750997Sbostic return (NULL); 44850997Sbostic bufp->ovfl->flags |= BUF_MOD; 44946372Sbostic #ifdef DEBUG1 45050997Sbostic (void)fprintf(stderr, "ADDOVFLPAGE: %d->ovfl was %d is now %d\n", 45150997Sbostic tmp1, tmp2, bufp->ovfl->addr); 45246372Sbostic #endif 45350997Sbostic ndx = sp[0]; 45450997Sbostic /* 45550997Sbostic * Since a pair is allocated on a page only if there's room to add 45650997Sbostic * an overflow page, we know that the OVFL information will fit on 45750997Sbostic * the page. 45850997Sbostic */ 45950997Sbostic sp[ndx + 4] = OFFSET(sp); 46050997Sbostic sp[ndx + 3] = FREESPACE(sp) - OVFLSIZE; 46150997Sbostic sp[ndx + 1] = ovfl_num; 46250997Sbostic sp[ndx + 2] = OVFLPAGE; 46350997Sbostic sp[0] = ndx + 2; 46446372Sbostic #ifdef HASH_STATISTICS 46550997Sbostic hash_overflows++; 46646372Sbostic #endif 46750997Sbostic return (bufp->ovfl); 46846372Sbostic } 46946372Sbostic 47046372Sbostic /* 47150997Sbostic * Returns: 47250997Sbostic * 0 indicates SUCCESS 47350997Sbostic * -1 indicates FAILURE 47450997Sbostic */ 47550997Sbostic extern int 47650997Sbostic __get_page(p, bucket, is_bucket, is_disk, is_bitmap) 47750997Sbostic char *p; 47850997Sbostic u_int bucket; 47950997Sbostic int is_bucket, is_disk, is_bitmap; 48046372Sbostic { 48150997Sbostic register int fd, page, size; 48250997Sbostic int rsize; 48350997Sbostic u_short *bp; 48446372Sbostic 48550997Sbostic fd = hashp->fp; 48650997Sbostic size = hashp->BSIZE; 48746372Sbostic 48850997Sbostic if ((fd == -1) || !is_disk) { 48950997Sbostic PAGE_INIT(p); 49050997Sbostic return (0); 49150997Sbostic } 49250997Sbostic if (is_bucket) 49350997Sbostic page = BUCKET_TO_PAGE(bucket); 49450997Sbostic else 49550997Sbostic page = OADDR_TO_PAGE(bucket); 49655314Sbostic if ((lseek(fd, (off_t)page << hashp->BSHIFT, SEEK_SET) == -1) || 49750997Sbostic ((rsize = read(fd, p, size)) == -1)) 49850997Sbostic return (-1); 49950997Sbostic bp = (u_short *)p; 50050997Sbostic if (!rsize) 50150997Sbostic bp[0] = 0; /* We hit the EOF, so initialize a new page */ 50250997Sbostic else 50350997Sbostic if (rsize != size) { 50450997Sbostic errno = EFTYPE; 50550997Sbostic return (-1); 50650997Sbostic } 50753403Sbostic if (!is_bitmap && !bp[0]) { 50850997Sbostic PAGE_INIT(p); 50950997Sbostic } else 51050997Sbostic if (hashp->LORDER != BYTE_ORDER) { 51150997Sbostic register int i, max; 51246372Sbostic 51350997Sbostic if (is_bitmap) { 51450997Sbostic max = hashp->BSIZE >> 2; /* divide by 4 */ 51550997Sbostic for (i = 0; i < max; i++) 51650997Sbostic BLSWAP(((long *)p)[i]); 51750997Sbostic } else { 51850997Sbostic BSSWAP(bp[0]); 51950997Sbostic max = bp[0] + 2; 52050997Sbostic for (i = 1; i <= max; i++) 52150997Sbostic BSSWAP(bp[i]); 52250997Sbostic } 52350997Sbostic } 52450997Sbostic return (0); 52546372Sbostic } 52646372Sbostic 52750997Sbostic /* 52850997Sbostic * Write page p to disk 52950997Sbostic * 53050997Sbostic * Returns: 53150997Sbostic * 0 ==> OK 53250997Sbostic * -1 ==>failure 53350997Sbostic */ 53446372Sbostic extern int 53550997Sbostic __put_page(p, bucket, is_bucket, is_bitmap) 53650997Sbostic char *p; 53750997Sbostic u_int bucket; 53850997Sbostic int is_bucket, is_bitmap; 53946372Sbostic { 54050997Sbostic register int fd, page, size; 54150997Sbostic int wsize; 54246372Sbostic 54350997Sbostic size = hashp->BSIZE; 54450997Sbostic if ((hashp->fp == -1) && open_temp()) 54551073Sbostic return (-1); 54650997Sbostic fd = hashp->fp; 54746372Sbostic 54850997Sbostic if (hashp->LORDER != BYTE_ORDER) { 54950997Sbostic register int i; 55050997Sbostic register int max; 55146372Sbostic 55250997Sbostic if (is_bitmap) { 55350997Sbostic max = hashp->BSIZE >> 2; /* divide by 4 */ 55450997Sbostic for (i = 0; i < max; i++) 55550997Sbostic BLSWAP(((long *)p)[i]); 55650997Sbostic } else { 55750997Sbostic max = ((u_short *)p)[0] + 2; 55850997Sbostic for (i = 0; i <= max; i++) 55950997Sbostic BSSWAP(((u_short *)p)[i]); 56050997Sbostic } 56146372Sbostic } 56250997Sbostic if (is_bucket) 56350997Sbostic page = BUCKET_TO_PAGE(bucket); 56450997Sbostic else 56550997Sbostic page = OADDR_TO_PAGE(bucket); 56655314Sbostic if ((lseek(fd, (off_t)page << hashp->BSHIFT, SEEK_SET) == -1) || 56750997Sbostic ((wsize = write(fd, p, size)) == -1)) 56850997Sbostic /* Errno is set */ 56950997Sbostic return (-1); 57050997Sbostic if (wsize != size) { 57150997Sbostic errno = EFTYPE; 57250997Sbostic return (-1); 57350997Sbostic } 57450997Sbostic return (0); 57546372Sbostic } 57650997Sbostic 57746372Sbostic #define BYTE_MASK ((1 << INT_BYTE_SHIFT) -1) 57846372Sbostic /* 57950997Sbostic * Initialize a new bitmap page. Bitmap pages are left in memory 58050997Sbostic * once they are read in. 58150997Sbostic */ 58251055Sbostic extern int 58346372Sbostic __init_bitmap(pnum, nbits, ndx) 58450997Sbostic int pnum, nbits, ndx; 58546372Sbostic { 58650997Sbostic u_long *ip; 58750997Sbostic int clearbytes, clearints; 58846372Sbostic 58950997Sbostic if (!(ip = malloc(hashp->BSIZE))) 59051055Sbostic return (1); 59150997Sbostic hashp->nmaps++; 59250997Sbostic clearints = ((nbits - 1) >> INT_BYTE_SHIFT) + 1; 59350997Sbostic clearbytes = clearints << INT_TO_BYTE; 59451055Sbostic (void)memset((char *)ip, 0, clearbytes); 59551055Sbostic (void)memset(((char *)ip) + clearbytes, 0xFF, 59650997Sbostic hashp->BSIZE - clearbytes); 59750997Sbostic ip[clearints - 1] = ALL_SET << (nbits & BYTE_MASK); 59850997Sbostic SETBIT(ip, 0); 59950997Sbostic hashp->BITMAPS[ndx] = (u_short)pnum; 60050997Sbostic hashp->mapp[ndx] = ip; 60151055Sbostic return (0); 60246372Sbostic } 60350997Sbostic 60451055Sbostic static u_long 60550997Sbostic first_free(map) 60650997Sbostic u_long map; 60746372Sbostic { 60850997Sbostic register u_long i, mask; 60946372Sbostic 61050997Sbostic mask = 0x1; 61150997Sbostic for (i = 0; i < BITS_PER_MAP; i++) { 61250997Sbostic if (!(mask & map)) 61350997Sbostic return (i); 61450997Sbostic mask = mask << 1; 61550997Sbostic } 61650997Sbostic return (i); 61746372Sbostic } 61846372Sbostic 61950997Sbostic static u_short 62050997Sbostic overflow_page() 62146372Sbostic { 62250997Sbostic register u_long *freep; 62350997Sbostic register int max_free, offset, splitnum; 62450997Sbostic u_short addr; 62551061Sbostic int bit, first_page, free_bit, free_page, i, in_use_bits, j; 62646372Sbostic #ifdef DEBUG2 62750997Sbostic int tmp1, tmp2; 62846372Sbostic #endif 62951061Sbostic splitnum = hashp->OVFL_POINT; 63050997Sbostic max_free = hashp->SPARES[splitnum]; 63146372Sbostic 63250997Sbostic free_page = (max_free - 1) >> (hashp->BSHIFT + BYTE_SHIFT); 63350997Sbostic free_bit = (max_free - 1) & ((hashp->BSIZE << BYTE_SHIFT) - 1); 63446372Sbostic 63550997Sbostic /* Look through all the free maps to find the first free block */ 63651061Sbostic first_page = hashp->LAST_FREED >>(hashp->BSHIFT + BYTE_SHIFT); 63751061Sbostic for ( i = first_page; i <= free_page; i++ ) { 63850997Sbostic if (!(freep = (u_long *)hashp->mapp[i]) && 63950997Sbostic !(freep = fetch_bitmap(i))) 64050997Sbostic return (NULL); 64150997Sbostic if (i == free_page) 64250997Sbostic in_use_bits = free_bit; 64350997Sbostic else 64450997Sbostic in_use_bits = (hashp->BSIZE << BYTE_SHIFT) - 1; 64551061Sbostic 64651061Sbostic if (i == first_page) { 64751061Sbostic bit = hashp->LAST_FREED & 64851061Sbostic ((hashp->BSIZE << BYTE_SHIFT) - 1); 64951061Sbostic j = bit / BITS_PER_MAP; 65051061Sbostic bit = bit & ~(BITS_PER_MAP - 1); 65151061Sbostic } else { 65251061Sbostic bit = 0; 65351061Sbostic j = 0; 65451061Sbostic } 65551061Sbostic for (; bit <= in_use_bits; j++, bit += BITS_PER_MAP) 65650997Sbostic if (freep[j] != ALL_SET) 65750997Sbostic goto found; 65847250Sbostic } 65946372Sbostic 66050997Sbostic /* No Free Page Found */ 66151061Sbostic hashp->LAST_FREED = hashp->SPARES[splitnum]; 66250997Sbostic hashp->SPARES[splitnum]++; 66350997Sbostic offset = hashp->SPARES[splitnum] - 66450997Sbostic (splitnum ? hashp->SPARES[splitnum - 1] : 0); 66546372Sbostic 66651061Sbostic #define OVMSG "HASH: Out of overflow pages. Increase page size\n" 66751061Sbostic if (offset > SPLITMASK) { 66851061Sbostic if (++splitnum >= NCACHED) { 66951061Sbostic (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); 67051061Sbostic return (NULL); 67151061Sbostic } 67251061Sbostic hashp->OVFL_POINT = splitnum; 67351061Sbostic hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1]; 67451061Sbostic hashp->SPARES[splitnum-1]--; 67551061Sbostic offset = 0; 67651061Sbostic } 67751061Sbostic 67850997Sbostic /* Check if we need to allocate a new bitmap page */ 67950997Sbostic if (free_bit == (hashp->BSIZE << BYTE_SHIFT) - 1) { 68050997Sbostic free_page++; 68150997Sbostic if (free_page >= NCACHED) { 68250997Sbostic (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); 68350997Sbostic return (NULL); 68450997Sbostic } 68550997Sbostic /* 68650997Sbostic * This is tricky. The 1 indicates that you want the new page 68750997Sbostic * allocated with 1 clear bit. Actually, you are going to 68850997Sbostic * allocate 2 pages from this map. The first is going to be 68950997Sbostic * the map page, the second is the overflow page we were 69050997Sbostic * looking for. The init_bitmap routine automatically, sets 69150997Sbostic * the first bit of itself to indicate that the bitmap itself 69250997Sbostic * is in use. We would explicitly set the second bit, but 69350997Sbostic * don't have to if we tell init_bitmap not to leave it clear 69450997Sbostic * in the first place. 69550997Sbostic */ 69651055Sbostic if (__init_bitmap((int)OADDR_OF(splitnum, offset), 69751055Sbostic 1, free_page)) 69851055Sbostic return (NULL); 69950997Sbostic hashp->SPARES[splitnum]++; 70046372Sbostic #ifdef DEBUG2 70150997Sbostic free_bit = 2; 70246372Sbostic #endif 70350997Sbostic offset++; 70451061Sbostic if (offset > SPLITMASK) { 70551061Sbostic if (++splitnum >= NCACHED) { 70651061Sbostic (void)write(STDERR_FILENO, OVMSG, 70751061Sbostic sizeof(OVMSG) - 1); 70851061Sbostic return (NULL); 70951061Sbostic } 71051061Sbostic hashp->OVFL_POINT = splitnum; 71151061Sbostic hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1]; 71251061Sbostic hashp->SPARES[splitnum-1]--; 71351061Sbostic offset = 0; 71451061Sbostic } 71550997Sbostic } else { 71650997Sbostic /* 71750997Sbostic * Free_bit addresses the last used bit. Bump it to address 71850997Sbostic * the first available bit. 71950997Sbostic */ 72050997Sbostic free_bit++; 72150997Sbostic SETBIT(freep, free_bit); 72250997Sbostic } 72346372Sbostic 72450997Sbostic /* Calculate address of the new overflow page */ 72550997Sbostic addr = OADDR_OF(splitnum, offset); 72646372Sbostic #ifdef DEBUG2 72750997Sbostic (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", 72850997Sbostic addr, free_bit, free_page); 72946372Sbostic #endif 73050997Sbostic return (addr); 73146372Sbostic 73246372Sbostic found: 73350997Sbostic bit = bit + first_free(freep[j]); 73450997Sbostic SETBIT(freep, bit); 73546372Sbostic #ifdef DEBUG2 73650997Sbostic tmp1 = bit; 73750997Sbostic tmp2 = i; 73846372Sbostic #endif 73950997Sbostic /* 74050997Sbostic * Bits are addressed starting with 0, but overflow pages are addressed 74150997Sbostic * beginning at 1. Bit is a bit addressnumber, so we need to increment 74250997Sbostic * it to convert it to a page number. 74350997Sbostic */ 74450997Sbostic bit = 1 + bit + (i * (hashp->BSIZE << BYTE_SHIFT)); 74551061Sbostic if (bit >= hashp->LAST_FREED) 74651061Sbostic hashp->LAST_FREED = bit - 1; 74746372Sbostic 74850997Sbostic /* Calculate the split number for this page */ 74950997Sbostic for (i = 0; (i < splitnum) && (bit > hashp->SPARES[i]); i++); 75050997Sbostic offset = (i ? bit - hashp->SPARES[i - 1] : bit); 75150997Sbostic if (offset >= SPLITMASK) 75250997Sbostic return (NULL); /* Out of overflow pages */ 75350997Sbostic addr = OADDR_OF(i, offset); 75446372Sbostic #ifdef DEBUG2 75550997Sbostic (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", 75650997Sbostic addr, tmp1, tmp2); 75746372Sbostic #endif 75846372Sbostic 75950997Sbostic /* Allocate and return the overflow page */ 76050997Sbostic return (addr); 76146372Sbostic } 76246372Sbostic 76346372Sbostic /* 76450997Sbostic * Mark this overflow page as free. 76550997Sbostic */ 76650997Sbostic extern void 76750997Sbostic __free_ovflpage(obufp) 76850997Sbostic BUFHEAD *obufp; 76946372Sbostic { 77050997Sbostic register u_short addr; 77150997Sbostic u_long *freep; 77250997Sbostic int bit_address, free_page, free_bit; 77350997Sbostic u_short ndx; 77446372Sbostic 77550997Sbostic addr = obufp->addr; 77646372Sbostic #ifdef DEBUG1 77750997Sbostic (void)fprintf(stderr, "Freeing %d\n", addr); 77846372Sbostic #endif 77950997Sbostic ndx = (((u_short)addr) >> SPLITSHIFT); 78050997Sbostic bit_address = 78150997Sbostic (ndx ? hashp->SPARES[ndx - 1] : 0) + (addr & SPLITMASK) - 1; 78251061Sbostic if (bit_address < hashp->LAST_FREED) 78351061Sbostic hashp->LAST_FREED = bit_address; 78450997Sbostic free_page = (bit_address >> (hashp->BSHIFT + BYTE_SHIFT)); 78550997Sbostic free_bit = bit_address & ((hashp->BSIZE << BYTE_SHIFT) - 1); 78646372Sbostic 78750997Sbostic if (!(freep = hashp->mapp[free_page])) 78850997Sbostic freep = fetch_bitmap(free_page); 78950997Sbostic #ifdef DEBUG 79050997Sbostic /* 79150997Sbostic * This had better never happen. It means we tried to read a bitmap 79250997Sbostic * that has already had overflow pages allocated off it, and we 79350997Sbostic * failed to read it from the file. 79450997Sbostic */ 79550997Sbostic if (!freep) 79650997Sbostic assert(0); 79750997Sbostic #endif 79850997Sbostic CLRBIT(freep, free_bit); 79946372Sbostic #ifdef DEBUG2 80050997Sbostic (void)fprintf(stderr, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n", 80150997Sbostic obufp->addr, free_bit, free_page); 80246372Sbostic #endif 80350997Sbostic __reclaim_buf(obufp); 80446372Sbostic } 80546372Sbostic 80646372Sbostic /* 80750997Sbostic * Returns: 80850997Sbostic * 0 success 80950997Sbostic * -1 failure 81050997Sbostic */ 81146372Sbostic static int 81246372Sbostic open_temp() 81346372Sbostic { 81450997Sbostic sigset_t set, oset; 81550997Sbostic static char namestr[] = "_hashXXXXXX"; 81646372Sbostic 81750997Sbostic /* Block signals; make sure file goes away at process exit. */ 818*55315Sbostic (void)sigfillset(&set); 81950997Sbostic (void)sigprocmask(SIG_BLOCK, &set, &oset); 82050997Sbostic if ((hashp->fp = mkstemp(namestr)) != -1) { 82150997Sbostic (void)unlink(namestr); 82250997Sbostic (void)fcntl(hashp->fp, F_SETFD, 1); 82350997Sbostic } 82450997Sbostic (void)sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL); 82550997Sbostic return (hashp->fp != -1 ? 0 : -1); 82646372Sbostic } 82746372Sbostic 82850997Sbostic /* 82950997Sbostic * We have to know that the key will fit, but the last entry on the page is 83050997Sbostic * an overflow pair, so we need to shift things. 83150997Sbostic */ 83246372Sbostic static void 83350997Sbostic squeeze_key(sp, key, val) 83450997Sbostic u_short *sp; 83550997Sbostic const DBT *key, *val; 83646372Sbostic { 83750997Sbostic register char *p; 83850997Sbostic u_short free_space, n, off, pageno; 83946372Sbostic 84050997Sbostic p = (char *)sp; 84150997Sbostic n = sp[0]; 84250997Sbostic free_space = FREESPACE(sp); 84350997Sbostic off = OFFSET(sp); 84446372Sbostic 84550997Sbostic pageno = sp[n - 1]; 84650997Sbostic off -= key->size; 84750997Sbostic sp[n - 1] = off; 84850997Sbostic bcopy(key->data, p + off, key->size); 84950997Sbostic off -= val->size; 85050997Sbostic sp[n] = off; 85150997Sbostic bcopy(val->data, p + off, val->size); 85250997Sbostic sp[0] = n + 2; 85350997Sbostic sp[n + 1] = pageno; 85450997Sbostic sp[n + 2] = OVFLPAGE; 85550997Sbostic FREESPACE(sp) = free_space - PAIRSIZE(key, val); 85650997Sbostic OFFSET(sp) = off; 85746372Sbostic } 85846372Sbostic 85947250Sbostic static u_long * 86050997Sbostic fetch_bitmap(ndx) 86150997Sbostic int ndx; 86247250Sbostic { 86350997Sbostic if (ndx >= hashp->nmaps || 86450997Sbostic !(hashp->mapp[ndx] = malloc(hashp->BSIZE)) || 86550997Sbostic __get_page((char *)hashp->mapp[ndx], 86650997Sbostic hashp->BITMAPS[ndx], 0, 1, 1)) 86750997Sbostic return (NULL); 86850997Sbostic return (hashp->mapp[ndx]); 86950997Sbostic } 87047250Sbostic 87146372Sbostic #ifdef DEBUG4 87250997Sbostic int 87350997Sbostic print_chain(addr) 87450997Sbostic int addr; 87546372Sbostic { 87650997Sbostic BUFHEAD *bufp; 87750997Sbostic short *bp, oaddr; 87846372Sbostic 87950997Sbostic (void)fprintf(stderr, "%d ", addr); 88050997Sbostic bufp = __get_buf(addr, NULL, 0); 88146372Sbostic bp = (short *)bufp->page; 88250997Sbostic while (bp[0] && ((bp[bp[0]] == OVFLPAGE) || 88350997Sbostic ((bp[0] > 2) && bp[2] < REAL_KEY))) { 88450997Sbostic oaddr = bp[bp[0] - 1]; 88550997Sbostic (void)fprintf(stderr, "%d ", (int)oaddr); 88650997Sbostic bufp = __get_buf((int)oaddr, bufp, 0); 88746372Sbostic bp = (short *)bufp->page; 88846372Sbostic } 88950997Sbostic (void)fprintf(stderr, "\n"); 89046372Sbostic } 89146372Sbostic #endif 892